library(swimplot) library(grid) library(gtable) library(readr) library(mosaic) library(dplyr) library(survival) library(survminer) library(ggplot2) library(scales) library(coxphf) library(ggthemes) library(tidyverse) library(gtsummary) library(flextable) library(parameters) library(car) library(ComplexHeatmap) library(tidyverse) library(readxl) library(survival) library(janitor) library(DT)

#ctDNA positivity by stage and window

#Number of Pts at Baseline - percentage positivity by stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.prenac!="",]
circ_datadf <- as.data.frame(circ_data)

total_base <- sum(!is.na(circ_data$ctDNA.prenac))
print(total_base)
[1] 28
circ_data$ctDNA.prenac <- as.factor(circ_data$ctDNA.prenac)
cont_table_base <- table(circ_data$cStage, circ_data$ctDNA.prenac)
print(cont_table_base)
     
      NEGATIVE POSITIVE
  I          1        1
  II         0        3
  III        0       15
  IV         0        8
#Number of Pts post NAT - percentage positivity by stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.postnac!="",]
circ_datadf <- as.data.frame(circ_data)

total_postnat <- sum(!is.na(circ_data$ctDNA.postnac))
print(total_postnat)
[1] 27
circ_data$ctDNA.postnac <- as.factor(circ_data$ctDNA.postnac)
cont_table_postnat <- table(circ_data$cStage, circ_data$ctDNA.postnac)
print(cont_table_postnat)
     
      NEGATIVE POSITIVE
  I          2        0
  II         1        2
  III       10        4
  IV         5        3
#Number of Pts post-surgery - percentage positivity by stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.4w!="",]
circ_datadf <- as.data.frame(circ_data)

total_mrd <- sum(!is.na(circ_data$ctDNA.4w))
print(total_mrd)
[1] 28
circ_data$ctDNA.4w <- as.factor(circ_data$ctDNA.4w)
cont_table_mrd <- table(circ_data$cStage, circ_data$ctDNA.4w)
print(cont_table_mrd)
     
      NEGATIVE POSITIVE
  I          2        0
  II         2        1
  III       14        1
  IV         7        1

#Summary Table

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_datadf <- as.data.frame(circ_data)

circ_data_subset <- circ_data %>%
  select(
    Gender,
    Age,
    PS,
    cStage,
    TNM.Detailed,
    Neo.Regiment,
    ypStage,
    ypTNM.Detailed,
    TRG,
    pCR,
    Adj.Regimen,
    RFS.Event,
    OS.Event,
    RFS.months,
    OS.months) %>%
  mutate(
    Gender = factor(Gender),
    Age = as.numeric(Age),
    PS = factor(PS),
    cStage = factor(cStage),
    TNM.Detailed = factor(TNM.Detailed),
    Neo.Regiment = factor(Neo.Regiment),
    ypStage = factor(ypStage),
    ypTNM.Detailed = factor(ypTNM.Detailed),
    TRG = factor(TRG),
    pCR = factor(pCR),
    Adj.Regimen = factor(Adj.Regimen),
    RFS.Event = factor(RFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Recurrence", "Recurrence")),
    OS.Event = factor(OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased")),
    RFS.months = as.numeric(RFS.months),
    OS.months = as.numeric(OS.months))
table1 <- circ_data_subset %>%
  tbl_summary(
    statistic = list(
      all_continuous() ~ "{median} ({min} - {max})",
      all_categorical() ~ "{n} ({p}%)")) %>%
  bold_labels()
table1
Characteristic N = 281
Gender
    FEMALE 1 (3.6%)
    MALE 27 (96%)
Age 71 (50 - 82)
PS
    0 26 (93%)
    1 2 (7.1%)
cStage
    I 2 (7.1%)
    II 3 (11%)
    III 15 (54%)
    IV 8 (29%)
TNM.Detailed
    T1-T2N1M0 3 (11%)
    T3-T4N2-N3M0 3 (11%)
    T3-T4N2-N3M1 4 (14%)
    T3N0M0 2 (7.1%)
    T3N0M1 1 (3.6%)
    T3N1-N2M0 15 (54%)
Neo.Regiment
    DCF 19 (68%)
    FOLFOX6 6 (21%)
    FP 3 (11%)
ypStage
    0 4 (14%)
    I 3 (11%)
    II 8 (29%)
    III 7 (25%)
    IV 6 (21%)
ypTNM.Detailed
    T0-TisN0M0 4 (14%)
    T1-T3N0M0 6 (21%)
    T1-T3N1-N3M1 5 (18%)
    T1-T3N1M0 8 (29%)
    T1N0M0 2 (7.1%)
    T3N2-N3M0 3 (11%)
TRG
    1 12 (43%)
    2 13 (46%)
    3 3 (11%)
pCR
    FALSE 24 (86%)
    TRUE 4 (14%)
Adj.Regimen
     25 (89%)
    Nivolumab 3 (11%)
RFS.Event
    No Recurrence 16 (57%)
    Recurrence 12 (43%)
OS.Event
    Alive 26 (93%)
    Deceased 2 (7.1%)
RFS.months 20 (2 - 36)
OS.months 22 (6 - 34)
1 n (%); Median (Range)
fit1 <- as_flex_table(
  table1,
  include = everything(),
  return_calls = FALSE,
  strip_md_bold = TRUE)
Warning: The `strip_md_bold` argument of `as_flex_table()` is deprecated as of gtsummary 1.6.0.
fit1

Characteristic

N = 281

Gender

FEMALE

1 (3.6%)

MALE

27 (96%)

Age

71 (50 - 82)

PS

0

26 (93%)

1

2 (7.1%)

cStage

I

2 (7.1%)

II

3 (11%)

III

15 (54%)

IV

8 (29%)

TNM.Detailed

T1-T2N1M0

3 (11%)

T3-T4N2-N3M0

3 (11%)

T3-T4N2-N3M1

4 (14%)

T3N0M0

2 (7.1%)

T3N0M1

1 (3.6%)

T3N1-N2M0

15 (54%)

Neo.Regiment

DCF

19 (68%)

FOLFOX6

6 (21%)

FP

3 (11%)

ypStage

0

4 (14%)

I

3 (11%)

II

8 (29%)

III

7 (25%)

IV

6 (21%)

ypTNM.Detailed

T0-TisN0M0

4 (14%)

T1-T3N0M0

6 (21%)

T1-T3N1-N3M1

5 (18%)

T1-T3N1M0

8 (29%)

T1N0M0

2 (7.1%)

T3N2-N3M0

3 (11%)

TRG

1

12 (43%)

2

13 (46%)

3

3 (11%)

pCR

FALSE

24 (86%)

TRUE

4 (14%)

Adj.Regimen

25 (89%)

Nivolumab

3 (11%)

RFS.Event

No Recurrence

16 (57%)

Recurrence

12 (43%)

OS.Event

Alive

26 (93%)

Deceased

2 (7.1%)

RFS.months

20 (2 - 36)

OS.months

22 (6 - 34)

1n (%); Median (Range)

save_as_docx(fit1, path= "~/Downloads/table1.docx")

#Heatmap for the clinical factors

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_datadf <- as.data.frame(circ_data)
circ_data <- circ_data %>% arrange(cStage)
circ_datadf <- as.data.frame(circ_data)

ha <- HeatmapAnnotation(
  cStage = circ_data$cStage,
  Gender = circ_data$Gender,
  Neo.Regiment = circ_data$Neo.Regiment,
  TRG = circ_data$TRG,
  pCR = circ_data$pCR,
  ypStage = circ_data$ypStage,
  Adj.Regimen = circ_data$Adj.Regimen,
  ctDNA.prenac = circ_data$ctDNA.prenac,
  ctDNA.postnac = circ_data$ctDNA.postnac,
  ctDNA.4w = circ_data$ctDNA.4w,
  ctDNA.12w = circ_data$ctDNA.12w,
  ctDNA.24w = circ_data$ctDNA.24w,
  ctDNA.36w = circ_data$ctDNA.36w,
  ctDNA.48w = circ_data$ctDNA.48w,
  ctDNA.postMRD = circ_data$ctDNA.postMRD,
  RFS.Event = circ_data$RFS.Event,
  OS.Event = circ_data$OS.Event,
  
    col = list(cStage = c("I" = "seagreen1", "II" = "khaki", "III" = "orange", "IV" = "darkmagenta"),
    Gender = c("FEMALE" = "goldenrod" , "MALE" = "blue4"),
    Neo.Regiment = c("FOLFOX6" = "coral", "FP" ="darkgreen", "DCF" ="brown"),
    TRG = c("1" = "yellow3", "2" ="darkgreen", "3" = "brown2"),
    pCR = c("TRUE" = "lightblue", "FALSE" ="orange"),
    ypStage = c("0" = "khaki","I" = "seagreen2", "II" = "cornflowerblue","III" = "orange","IV" ="darkmagenta"),
    Adj.Regimen = c("Nivolumab" = "darkmagenta"),
    ctDNA.prenac = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    ctDNA.postnac = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    ctDNA.4w = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    ctDNA.12w = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    ctDNA.24w = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    ctDNA.36w = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    ctDNA.48w = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    ctDNA.postMRD = c("POSITIVE" = "red3", "NEGATIVE" ="blue"),
    RFS.Event = c("TRUE" = "red3", "FALSE" ="blue"),
    OS.Event = c("TRUE" = "black", "FALSE" ="grey")
)
)
ht <- Heatmap(matrix(nrow = 0, ncol = length(circ_data$cStage)),show_row_names = FALSE,cluster_rows = F,cluster_columns = FALSE, top_annotation = ha)
pdf("heatmap.pdf",width = 7, height = 7)
draw(ht, annotation_legend_side = "bottom")
dev.off()

#Overview plot

setwd("~/Downloads")
clinstage<- read.csv("NCC_ESCC_OP.csv")
clinstage_df<- as.data.frame(clinstage)

#Display the swimmer plot with the label box
oplot<-swimmer_plot(df=clinstage_df,
                    id='PatientName',
                    end='fu.diff.months',
                    fill='gray',
                    width=.01,)
oplot <- oplot + theme(panel.border = element_blank())
oplot <- oplot + scale_y_continuous(breaks = seq(-12, 108, by = 6))
oplot <- oplot + labs(x ="Patients" , y="Months from Surgery")
oplot


##plot events
oplot_ev1 <- oplot + swimmer_points(df_points=clinstage_df,
                                    id='PatientName',
                                    time='date.diff.months',
                                    name_shape ='Event_type',
                                    name_col = 'Event',
                                    size=3.5,fill='black',
                                    #col='darkgreen'
)
oplot_ev1


#Shape customization to Event_type

oplot_ev1.1 <- oplot_ev1 + ggplot2::scale_shape_manual(name="Event_type",values=c(1,16,6,18,18,4),breaks=c('ctDNA_neg','ctDNA_pos', 'Imaging','Surgery','Biopsy', 'Death'))

oplot_ev1.1


#plot treatment

oplot_ev2 <- oplot_ev1.1 + swimmer_lines(df_lines=clinstage_df,
                                         id='PatientName',
                                         start='Tx_start.months',
                                         end='Tx_end.months',
                                         name_col='Tx_type',
                                         size=3.5,
                                         name_alpha = 1.0)
oplot_ev2 <- oplot_ev2 + guides(linetype = guide_legend(override.aes = list(size = 5, color = "black")))
oplot_ev2  



#colour customization
#orange=NAC chemo,orangered=NAC ChemoIO,  purple=ACT, Black=Death, Red=PD, ctDNA negative=white, ctDNA positive=black, cystectomy=blue, TURBT=gray, ACT Chemo=purple, ACT IO=lightblue 
oplot_ev2.2 <- oplot_ev2 + ggplot2::scale_color_manual(name="Event",values=c("lightblue", "gray", "black", "black", "orange", "green", "red", "blue"))
oplot_ev2.2

#RFS by ctDNA post-NAT

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.postnac!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.postnac, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.postnac, data = circ_data)

                        n events median 0.95LCL 0.95UCL
ctDNA.postnac=NEGATIVE 18      5     NA      NA      NA
ctDNA.postnac=POSITIVE  9      7   7.85    3.88      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.postnac, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="RFS - ctDNA post-NAC", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(12, 24))
Call: survfit(formula = surv_object ~ ctDNA.postnac, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.postnac=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12     15       3    0.833  0.0878        0.568        0.943
   24      7       2    0.699  0.1141        0.417        0.864

                ctDNA.postnac=POSITIVE 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
      12.000        4.000        5.000        0.444        0.166        0.136        0.719 
circ_data$ctDNA.postnac <- factor(circ_data$ctDNA.postnac, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.postnac, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.postnac, data = circ_data)

  n= 27, number of events= 12 

                        coef exp(coef) se(coef)     z Pr(>|z|)  
ctDNA.postnacPOSITIVE 1.5174    4.5603   0.5893 2.575     0.01 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                      exp(coef) exp(-coef) lower .95 upper .95
ctDNA.postnacPOSITIVE      4.56     0.2193     1.437     14.47

Concordance= 0.698  (se = 0.064 )
Likelihood ratio test= 6.58  on 1 df,   p=0.01
Wald test            = 6.63  on 1 df,   p=0.01
Score (logrank) test = 7.95  on 1 df,   p=0.005
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 4.56 (1.44-14.47); p = 0.01"

#RFS by ctDNA Clearance post-NAC - 3 Groups

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.Clearance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.Clearance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.Clearance, data = circ_data)

                              n events median 0.95LCL 0.95UCL
ctDNA.Clearance=No Clearance  9      7   7.85    3.88      NA
ctDNA.Clearance=Sustained    13      2     NA      NA      NA
ctDNA.Clearance=Transient     4      3  15.70    8.35      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Clearance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("red","blue","green"), title="RFS - ctDNA Clearance post-NAC", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("No Clearance", "Sustained", "Transient"), legend.title="")

summary(KM_curve, times= c(12, 18, 24))
Call: survfit(formula = surv_object ~ ctDNA.Clearance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Clearance=No Clearance 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12      4       5    0.444   0.166       0.1359        0.719
   18      3       1    0.333   0.157       0.0783        0.623

                ctDNA.Clearance=Sustained 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12     11       2    0.846     0.1        0.512        0.959
   18      9       0    0.846     0.1        0.512        0.959
   24      7       0    0.846     0.1        0.512        0.959

                ctDNA.Clearance=Transient 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12      3       1    0.750   0.217        0.128        0.961
   18      1       1    0.375   0.286        0.011        0.808
circ_data$ctDNA.Clearance <- factor(circ_data$ctDNA.Clearance, levels=c("Sustained","Transient", "No Clearance"))
cox_fit <- coxph(surv_object ~ ctDNA.Clearance, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Clearance, data = circ_data)

  n= 26, number of events= 12 

                              coef exp(coef) se(coef)     z Pr(>|z|)   
ctDNA.ClearanceTransient    1.9042    6.7139   0.9275 2.053  0.04007 * 
ctDNA.ClearanceNo Clearance 2.1652    8.7160   0.8050 2.690  0.00715 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                            exp(coef) exp(-coef) lower .95 upper .95
ctDNA.ClearanceTransient        6.714     0.1489     1.090     41.35
ctDNA.ClearanceNo Clearance     8.716     0.1147     1.799     42.22

Concordance= 0.747  (se = 0.071 )
Likelihood ratio test= 10.2  on 2 df,   p=0.006
Wald test            = 7.33  on 2 df,   p=0.03
Score (logrank) test = 10.27  on 2 df,   p=0.006

#RFS by ctDNA at 4 weeks

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.4w!="",]

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.4w, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.4w, data = circ_data)

                   n events median 0.95LCL 0.95UCL
ctDNA.4w=NEGATIVE 25      9     NA   19.58      NA
ctDNA.4w=POSITIVE  3      3   2.83    1.87      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.4w, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="RFS - ctDNA at week 4", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

circ_data$ctDNA.4w <- factor(circ_data$ctDNA.4w, levels=c("NEGATIVE","POSITIVE"))
summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.4w, data = circ_data, conf.int = 0.95, 
    conf.type = "log-log")

                ctDNA.4w=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     25       0    1.000   0.000        1.000        1.000
   24      8       9    0.618   0.101        0.391        0.781

                ctDNA.4w=POSITIVE 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            3            0            1            0            1            1 
cox_fit <- coxph(surv_object ~ ctDNA.4w, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.4w, data = circ_data)

  n= 28, number of events= 12 

                   coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.4wPOSITIVE  2.609    13.585    0.778 3.353 0.000798 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                 exp(coef) exp(-coef) lower .95 upper .95
ctDNA.4wPOSITIVE     13.58    0.07361     2.957     62.42

Concordance= 0.635  (se = 0.062 )
Likelihood ratio test= 8.4  on 1 df,   p=0.004
Wald test            = 11.25  on 1 df,   p=8e-04
Score (logrank) test = 18.68  on 1 df,   p=2e-05
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 13.58 (2.96-62.42); p = 0.001"

#RFS by ctDNA MRD Window (2-16 weeks post-op)

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 23      7     NA   19.75      NA
ctDNA.MRD=POSITIVE  5      5   3.88    2.83      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="RFS - ctDNA MRD Window", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     23       0    1.000   0.000         1.00        1.000
   24      8       7    0.672   0.103         0.43        0.829

                ctDNA.MRD=POSITIVE 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            5            0            1            0            1            1 
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 28, number of events= 12 

                     coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.MRDPOSITIVE  3.4335   30.9862   0.8674 3.958 7.54e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     30.99    0.03227      5.66     169.6

Concordance= 0.723  (se = 0.06 )
Likelihood ratio test= 16.8  on 1 df,   p=4e-05
Wald test            = 15.67  on 1 df,   p=8e-05
Score (logrank) test = 32.96  on 1 df,   p=9e-09
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 30.99 (5.66-169.63); p = 0"

#RFS by ctDNA post MRD

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.postMRD!="",]

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.postMRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.postMRD, data = circ_data)

                        n events median 0.95LCL 0.95UCL
ctDNA.postMRD=NEGATIVE 17      3     NA      NA      NA
ctDNA.postMRD=POSITIVE  5      4   8.35    7.85      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.postMRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="RFS - ctDNA Surveillance (post-)MRD", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

circ_data$ctDNA.postMRD <- factor(circ_data$ctDNA.postMRD, levels=c("NEGATIVE","POSITIVE"))
summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.postMRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.postMRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     17       0    1.000  0.0000        1.000        1.000
   24      8       3    0.819  0.0946        0.538        0.938

                ctDNA.postMRD=POSITIVE 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            5            0            1            0            1            1 
cox_fit <- coxph(surv_object ~ ctDNA.postMRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.postMRD, data = circ_data)

  n= 22, number of events= 7 

                        coef exp(coef) se(coef)     z Pr(>|z|)   
ctDNA.postMRDPOSITIVE  3.177    23.975    1.156 2.748    0.006 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                      exp(coef) exp(-coef) lower .95 upper .95
ctDNA.postMRDPOSITIVE     23.97    0.04171     2.487     231.1

Concordance= 0.75  (se = 0.079 )
Likelihood ratio test= 10.18  on 1 df,   p=0.001
Wald test            = 7.55  on 1 df,   p=0.006
Score (logrank) test = 14.78  on 1 df,   p=1e-04
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 23.97 (2.49-231.13); p = 0.006"

#Time-dependent analysis for post-MRD (>16 weeks) time points

rm(list=ls())
setwd("~/Downloads")
dt <- read_xlsx("NCC ESCC Time Dependent Data_postMRD.xlsx") |>
  clean_names() |>
  mutate(across(.cols = c(window_start_date,dfs_date,
                          surveillance_1_date:surveillance_12_date), 
                .fns = ~ as_date(as.Date(.x, format = "%Y-%m-%d"))))

dt_biomarker <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date,
         surveillance_1_status:surveillance_12_date) |>
  filter(ct_dna_surveillance_available) |>
  pivot_longer(cols = surveillance_1_status:surveillance_12_date,
               names_to = c("visit_number", ".value"),
               names_pattern = "surveillance_(.)_(.*)") |>
  mutate(biomarker_time = day(days(date - window_start_date))) |>
  select(pts_id, biomarker_time, biomarker_status = status) |>
  filter(!is.na(biomarker_time))

glimpse(dt_biomarker)
Rows: 105
Columns: 3
$ pts_id           <chr> "SIG-E003", "SIG-E003", "SIG-E003", "SIG-E003", "SIG-E003", "SIG-E004", "SIG-E004", "SIG-E005", "SIG-E005", "SIG-E00…
$ biomarker_time   <dbl> -90, -40, 50, 141, 222, -102, -44, -90, -27, 57, 141, 218, -98, -49, 119, 245, -91, -34, 50, 144, 222, -100, -30, 54…
$ biomarker_status <chr> "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "POSITIVE", "NEGATIVE", "NEGATIVE", "NEGATIV…
dt_survival <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date:dfs_date, dfs_event) |>  # Added dfs_event here
  filter(ct_dna_surveillance_available) |>
  mutate(dfs_time = (dfs_date - window_start_date),
         dfs_time = day(days(dfs_time)),
         dfs_event = as.numeric(dfs_event)) |>
  select(pts_id, dfs_time, dfs_event)

glimpse(dt_survival)
Rows: 25
Columns: 3
$ pts_id    <chr> "SIG-E003", "SIG-E004", "SIG-E005", "SIG-E011", "SIG-E013", "SIG-E015", "SIG-E020", "SIG-E021", "SIG-E024", "SIG-E025", "SI…
$ dfs_time  <dbl> 966, -2, 966, 966, 784, 782, 827, 786, 119, -34, 620, 602, 604, -63, 295, 604, 49, 358, 476, 301, 606, 134, 415, 482, 52
$ dfs_event <dbl> 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1
aux <- dt_survival %>% 
  filter(dfs_time <= 0)

tab <- left_join(aux, dt) |>
  select(pts_id, window_start_date, dfs_time, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) |>
  select(pts_id, window_start_date, dfs_date, dfs_time)
Joining with `by = join_by(pts_id, dfs_event)`
datatable(tab, filter = "top")

dt_survival <- dt_survival |>
  filter(dfs_time > 0)

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date, 
                .fns = ~ .x < 0)) |>
  rowwise() |>
  mutate(sum_neg = 
           sum(c_across(surveillance_1_date:surveillance_12_date),
               na.rm = TRUE))  |>
  select(pts_id, sum_neg)

tab <- left_join(aux, dt) |>
  filter(sum_neg > 0) |>
  select(pts_id, sum_neg, window_start_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = window_start_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) 
Joining with `by = join_by(pts_id)`
datatable(tab, filter = "top")

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_2_date:surveillance_12_date,
                .fns = ~ dfs_date < .x)) |>
  rowwise() |>
  mutate(n_biomarker_after_event = sum(c_across(surveillance_2_date:
                                                  surveillance_12_date), 
                                       na.rm = TRUE)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date,
                .fns = ~ !is.na(.x))) |>
  mutate(total_biomarker = sum(c_across(surveillance_2_date:
                                          surveillance_12_date), 
                               na.rm = TRUE)) |>
  select(pts_id, n_biomarker_after_event, total_biomarker)

temp <- aux |> 
  select(-pts_id) |>
  group_by(n_biomarker_after_event, total_biomarker) |>
  summarize(freq = n())
`summarise()` has grouped output by 'n_biomarker_after_event'. You can override using the `.groups` argument.
tab <- left_join(aux, dt) |>
  select(pts_id, n_biomarker_after_event, total_biomarker, 
         dfs_date,
         surveillance_2_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) |>
  filter(n_biomarker_after_event > 0)
Joining with `by = join_by(pts_id)`
datatable(tab, filter = "top")

aux <- tmerge(data1 = dt_survival, 
              data2 = dt_survival,
              id = pts_id, 
              dfs_event = event(dfs_time, dfs_event))
dt_final <- tmerge(data1 = aux, 
                   data2 = dt_biomarker,
                   id = pts_id, 
                   biomarker_status = 
                     tdc(biomarker_time, biomarker_status))

datatable(dt_final, filter = "top")

# Syntax if there is not time-dependent covariate
# fit <- coxph(Surv(dfs_time, dfs_event) ~ biomarker_status,
#              data = dt_final)
# summary(fit)

fit <- coxph(Surv(tstart, tstop, dfs_event) ~ biomarker_status,
             data = dt_final)
summary(fit)
Call:
coxph(formula = Surv(tstart, tstop, dfs_event) ~ biomarker_status, 
    data = dt_final)

  n= 76, number of events= 7 

                           coef exp(coef) se(coef)     z Pr(>|z|)   
biomarker_statusPOSITIVE  3.433    30.965    1.102 3.115  0.00184 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                         exp(coef) exp(-coef) lower .95 upper .95
biomarker_statusPOSITIVE     30.96    0.03229     3.571     268.5

Concordance= 0.84  (se = 0.083 )
Likelihood ratio test= 15.55  on 1 df,   p=8e-05
Wald test            = 9.7  on 1 df,   p=0.002
Score (logrank) test = 21.42  on 1 df,   p=4e-06

#RFS by ctDNA anytime post-surgery

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.anytime!="",]

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.anytime, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.anytime, data = circ_data)

                        n events median 0.95LCL 0.95UCL
ctDNA.anytime=NEGATIVE 17      2     NA      NA      NA
ctDNA.anytime=POSITIVE 11     10   8.35    5.55      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.anytime, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="RFS - ctDNA anytime post-surgery", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

circ_data$ctDNA.anytime <- factor(circ_data$ctDNA.anytime, levels=c("NEGATIVE","POSITIVE"))
summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.anytime, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.anytime=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     17       0    1.000  0.0000        1.000        1.000
   24      8       2    0.882  0.0781        0.606        0.969

                ctDNA.anytime=POSITIVE 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0           11            0            1            0            1            1 
cox_fit <- coxph(surv_object ~ ctDNA.anytime, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.anytime, data = circ_data)

  n= 28, number of events= 12 

                         coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.anytimePOSITIVE  2.8517   17.3168   0.8012 3.559 0.000372 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                      exp(coef) exp(-coef) lower .95 upper .95
ctDNA.anytimePOSITIVE     17.32    0.05775     3.601     83.27

Concordance= 0.787  (se = 0.056 )
Likelihood ratio test= 18.5  on 1 df,   p=2e-05
Wald test            = 12.67  on 1 df,   p=4e-04
Score (logrank) test = 21.4  on 1 df,   p=4e-06
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 17.32 (3.6-83.27); p = 0"

#Time-dependent analysis for post-surgery time points

rm(list=ls())
setwd("~/Downloads")
dt <- read_xlsx("NCC ESCC Time Dependent Data_anytime postop.xlsx") |>
  clean_names() |>
  mutate(across(.cols = c(window_start_date,dfs_date,
                          surveillance_1_date:surveillance_12_date), 
                .fns = ~ as_date(as.Date(.x, format = "%Y-%m-%d"))))

dt_biomarker <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date,
         surveillance_1_status:surveillance_12_date) |>
  filter(ct_dna_surveillance_available) |>
  pivot_longer(cols = surveillance_1_status:surveillance_12_date,
               names_to = c("visit_number", ".value"),
               names_pattern = "surveillance_(.)_(.*)") |>
  mutate(biomarker_time = day(days(date - window_start_date))) |>
  select(pts_id, biomarker_time, biomarker_status = status) |>
  filter(!is.na(biomarker_time))

glimpse(dt_biomarker)
Rows: 117
Columns: 3
$ pts_id           <chr> "SIG-E003", "SIG-E003", "SIG-E003", "SIG-E003", "SIG-E003", "SIG-E004", "SIG-E004", "SIG-E005", "SIG-E005", "SIG-E00…
$ biomarker_time   <dbl> 30, 80, 170, 261, 342, 18, 76, 30, 93, 177, 261, 338, 22, 71, 239, 365, 29, 86, 170, 264, 342, 20, 90, 174, 258, 349…
$ biomarker_status <chr> "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "POSITIVE", "NEGATIVE", "NEGATIVE", "NEGATIV…
dt_survival <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date:dfs_date, dfs_event) |>  # Added dfs_event here
  filter(ct_dna_surveillance_available) |>
  mutate(dfs_time = (dfs_date - window_start_date),
         dfs_time = day(days(dfs_time)),
         dfs_event = as.numeric(dfs_event)) |>
  select(pts_id, dfs_time, dfs_event)

glimpse(dt_survival)
Rows: 28
Columns: 3
$ pts_id    <chr> "SIG-E003", "SIG-E004", "SIG-E005", "SIG-E011", "SIG-E013", "SIG-E015", "SIG-E020", "SIG-E021", "SIG-E024", "SIG-E025", "SI…
$ dfs_time  <dbl> 1086, 118, 1086, 1086, 904, 902, 947, 906, 239, 86, 740, 722, 724, 57, 415, 724, 169, 478, 596, 421, 726, 254, 535, 602, 34…
$ dfs_event <dbl> 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1
aux <- dt_survival %>% 
  filter(dfs_time <= 0)

tab <- left_join(aux, dt) |>
  select(pts_id, window_start_date, dfs_time, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) |>
  select(pts_id, window_start_date, dfs_date, dfs_time)
Joining with `by = join_by(pts_id, dfs_event)`
datatable(tab, filter = "top")

dt_survival <- dt_survival |>
  filter(dfs_time > 0)

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date, 
                .fns = ~ .x < 0)) |>
  rowwise() |>
  mutate(sum_neg = 
           sum(c_across(surveillance_1_date:surveillance_12_date),
               na.rm = TRUE))  |>
  select(pts_id, sum_neg)

tab <- left_join(aux, dt) |>
  filter(sum_neg > 0) |>
  select(pts_id, sum_neg, window_start_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = window_start_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) 
Joining with `by = join_by(pts_id)`
datatable(tab, filter = "top")

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_2_date:surveillance_12_date,
                .fns = ~ dfs_date < .x)) |>
  rowwise() |>
  mutate(n_biomarker_after_event = sum(c_across(surveillance_2_date:
                                                  surveillance_12_date), 
                                       na.rm = TRUE)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date,
                .fns = ~ !is.na(.x))) |>
  mutate(total_biomarker = sum(c_across(surveillance_2_date:
                                          surveillance_12_date), 
                               na.rm = TRUE)) |>
  select(pts_id, n_biomarker_after_event, total_biomarker)

temp <- aux |> 
  select(-pts_id) |>
  group_by(n_biomarker_after_event, total_biomarker) |>
  summarize(freq = n())
`summarise()` has grouped output by 'n_biomarker_after_event'. You can override using the `.groups` argument.
tab <- left_join(aux, dt) |>
  select(pts_id, n_biomarker_after_event, total_biomarker, 
         dfs_date,
         surveillance_2_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) |>
  filter(n_biomarker_after_event > 0)
Joining with `by = join_by(pts_id)`
datatable(tab, filter = "top")

aux <- tmerge(data1 = dt_survival, 
              data2 = dt_survival,
              id = pts_id, 
              dfs_event = event(dfs_time, dfs_event))
dt_final <- tmerge(data1 = aux, 
                   data2 = dt_biomarker,
                   id = pts_id, 
                   biomarker_status = 
                     tdc(biomarker_time, biomarker_status))

datatable(dt_final, filter = "top")

# Syntax if there is not time-dependent covariate
# fit <- coxph(Surv(dfs_time, dfs_event) ~ biomarker_status,
#              data = dt_final)
# summary(fit)

fit <- coxph(Surv(tstart, tstop, dfs_event) ~ biomarker_status,
             data = dt_final)
summary(fit)
Call:
coxph(formula = Surv(tstart, tstop, dfs_event) ~ biomarker_status, 
    data = dt_final)

  n= 113, number of events= 12 
   (28 observations deleted due to missingness)

                            coef exp(coef) se(coef)     z Pr(>|z|)    
biomarker_statusPOSITIVE  3.2855   26.7230   0.7845 4.188 2.81e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                         exp(coef) exp(-coef) lower .95 upper .95
biomarker_statusPOSITIVE     26.72    0.03742     5.743     124.3

Concordance= 0.843  (se = 0.059 )
Likelihood ratio test= 26.03  on 1 df,   p=3e-07
Wald test            = 17.54  on 1 df,   p=3e-05
Score (logrank) test = 38  on 1 df,   p=7e-10

#RFS by ctDNA Dynamics post-NAC to Week 4 - 3 groups

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")

circ_data$ctDNA.Dynamics <- NA #first we create the variable for the ctDNA & NAC combination, and we assign values
circ_data <- circ_data %>%
  mutate(ctDNA.Dynamics = case_when(
    ctDNA.postnac == "NEGATIVE" & ctDNA.4w == "NEGATIVE" ~ "1",
    ctDNA.postnac == "POSITIVE" & ctDNA.4w == "NEGATIVE" ~ "2",
    ctDNA.postnac == "POSITIVE" & ctDNA.4w == "POSITIVE" ~ "3"
  ))

circ_data <- circ_data[!is.na(circ_data$ctDNA.Dynamics),]
survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.Dynamics, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.Dynamics, data = circ_data)

                  n events median 0.95LCL 0.95UCL
ctDNA.Dynamics=1 17      4     NA      NA      NA
ctDNA.Dynamics=2  7      5  13.83    5.55      NA
ctDNA.Dynamics=3  2      2   2.35    1.87      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Dynamics, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","green","red"), title="RFS - ctDNA Dynamics post-NAC - Week 4", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("Persistently Negative","Converted Negative", "Persistently Positive"), legend.title="")

summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.Dynamics, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Dynamics=1 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     17       0     1.00   0.000        1.000        1.000
   24      7       4     0.74   0.113        0.443        0.895

                ctDNA.Dynamics=2 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            7            0            1            0            1            1 

                ctDNA.Dynamics=3 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            2            0            1            0            1            1 
circ_data$ctDNA.Dynamics <- factor(circ_data$ctDNA.Dynamics, levels=c("1","2","3"), labels=c("Persistently Negative","Converted Negative", "Persistently Positive"))
cox_fit <- coxphf(surv_object ~ ctDNA.Dynamics, data=circ_data)
summary(cox_fit)
coxphf(formula = surv_object ~ ctDNA.Dynamics, data = circ_data)

Model fitted by Penalized ML
Confidence intervals and p-values by Profile Likelihood 

                                        coef se(coef)  exp(coef) lower 0.95  upper 0.95    Chisq          p
ctDNA.DynamicsConverted Negative    1.415181 0.660610   4.117232   1.162499    15.37828  4.76164 0.02910081
ctDNA.DynamicsPersistently Positive 5.119773 1.952631 167.297395  11.566163 24434.33628 13.90506 0.00019228

Likelihood ratio test=16.3327 on 2 df, p=0.000284053, n=26
Wald test = 9.50381 on 2 df, p = 0.00863523

Covariance-Matrix:
                                    ctDNA.DynamicsConverted Negative ctDNA.DynamicsPersistently Positive
ctDNA.DynamicsConverted Negative                           0.4364055                           0.2744938
ctDNA.DynamicsPersistently Positive                        0.2744938                           3.8127677

#RFS by ctDNA Dynamics post-NAC to Week 4 - 4 groups

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")

circ_data$ctDNA.Dynamics <- NA #first we create the variable for the ctDNA & NAC combination, and we assign values
circ_data <- circ_data %>%
  mutate(ctDNA.Dynamics = case_when(
    ctDNA.postnac == "NEGATIVE" & ctDNA.4w == "NEGATIVE" ~ "1",
    ctDNA.postnac == "POSITIVE" & ctDNA.4w == "NEGATIVE" ~ "2",
    ctDNA.postnac == "NEGATIVE" & ctDNA.4w == "POSITIVE" ~ "3",
    ctDNA.postnac == "POSITIVE" & ctDNA.4w == "POSITIVE" ~ "4"
  ))

circ_data <- circ_data[!is.na(circ_data$ctDNA.Dynamics),]
survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.Dynamics, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.Dynamics, data = circ_data)

                  n events median 0.95LCL 0.95UCL
ctDNA.Dynamics=1 17      4     NA      NA      NA
ctDNA.Dynamics=2  7      5  13.83    5.55      NA
ctDNA.Dynamics=3  1      1   8.35      NA      NA
ctDNA.Dynamics=4  2      2   2.35    1.87      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Dynamics, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","green","purple","red"), title="RFS - ctDNA Dynamics post-NAC - Week 4", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("Persistently Negative","Converted Negative", "Converted Positive","Persistently Positive"), legend.title="")

summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.Dynamics, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Dynamics=1 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     17       0     1.00   0.000        1.000        1.000
   24      7       4     0.74   0.113        0.443        0.895

                ctDNA.Dynamics=2 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            7            0            1            0            1            1 

                ctDNA.Dynamics=3 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            1            0            1            0            1            1 

                ctDNA.Dynamics=4 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            2            0            1            0            1            1 
circ_data$ctDNA.Dynamics <- factor(circ_data$ctDNA.Dynamics, levels=c("1","2","3","4"), labels=c("Persistently Negative","Converted Negative","Converted Positive","Persistently Positive"))
cox_fit <- coxphf(surv_object ~ ctDNA.Dynamics, data=circ_data)
summary(cox_fit)
coxphf(formula = surv_object ~ ctDNA.Dynamics, data = circ_data)

Model fitted by Penalized ML
Confidence intervals and p-values by Profile Likelihood 

                                        coef  se(coef)  exp(coef) lower 0.95  upper 0.95     Chisq            p
ctDNA.DynamicsConverted Negative    1.440334 0.6697529   4.222106   1.192923    15.76026  4.928230 0.0264213641
ctDNA.DynamicsConverted Positive    2.517717 1.0853732  12.400250   1.153475    81.17250  4.214575 0.0400781097
ctDNA.DynamicsPersistently Positive 5.319678 1.9341003 204.318068  13.851382 30074.62703 14.722641 0.0001245417

Likelihood ratio test=19.40201 on 3 df, p=0.0002257542, n=27
Wald test = 12.0956 on 3 df, p = 0.007062756

Covariance-Matrix:
                                    ctDNA.DynamicsConverted Negative ctDNA.DynamicsConverted Positive ctDNA.DynamicsPersistently Positive
ctDNA.DynamicsConverted Negative                           0.4485690                        0.2580369                           0.2791462
ctDNA.DynamicsConverted Positive                           0.2580369                        1.1780349                           0.3771376
ctDNA.DynamicsPersistently Positive                        0.2791462                        0.3771376                           3.7407440

#RFS by ctDNA Dynamics post-NAC to MRD - 4 groups

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")

circ_data$ctDNA.Dynamics <- NA #first we create the variable for the ctDNA & NAC combination, and we assign values
circ_data <- circ_data %>%
  mutate(ctDNA.Dynamics = case_when(
    ctDNA.postnac == "NEGATIVE" & ctDNA.MRD == "NEGATIVE" ~ "1",
    ctDNA.postnac == "POSITIVE" & ctDNA.MRD == "NEGATIVE" ~ "2",
    ctDNA.postnac == "NEGATIVE" & ctDNA.MRD == "POSITIVE" ~ "3",
    ctDNA.postnac == "POSITIVE" & ctDNA.MRD == "POSITIVE" ~ "4"
  ))

circ_data <- circ_data[!is.na(circ_data$ctDNA.Dynamics),]
survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.Dynamics, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.Dynamics, data = circ_data)

                  n events median 0.95LCL 0.95UCL
ctDNA.Dynamics=1 17      4     NA      NA      NA
ctDNA.Dynamics=2  5      3  19.75   13.83      NA
ctDNA.Dynamics=3  1      1   8.35      NA      NA
ctDNA.Dynamics=4  4      4   3.35    1.87      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Dynamics, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","green","purple","red"), title="RFS - ctDNA Dynamics post-NAC - MRD Window", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", legend.labs=c("Persistently Negative","Converted Negative", "Converted Positive","Persistently Positive"), legend.title="")

summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.Dynamics, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Dynamics=1 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     17       0     1.00   0.000        1.000        1.000
   24      7       4     0.74   0.113        0.443        0.895

                ctDNA.Dynamics=2 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            5            0            1            0            1            1 

                ctDNA.Dynamics=3 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            1            0            1            0            1            1 

                ctDNA.Dynamics=4 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            4            0            1            0            1            1 
circ_data$ctDNA.Dynamics <- factor(circ_data$ctDNA.Dynamics, levels=c("1","2","3","4"), labels=c("Persistently Negative","Converted Negative","Converted Positive","Persistently Positive"))
cox_fit <- coxphf(surv_object ~ ctDNA.Dynamics, data=circ_data)
summary(cox_fit)
coxphf(formula = surv_object ~ ctDNA.Dynamics, data = circ_data)

Model fitted by Penalized ML
Confidence intervals and p-values by Profile Likelihood 

                                        coef  se(coef)  exp(coef) lower 0.95  upper 0.95     Chisq            p
ctDNA.DynamicsConverted Negative    1.050415 0.7518683   2.858836  0.6387374    11.75356  1.993070 1.580203e-01
ctDNA.DynamicsConverted Positive    2.819645 1.1734372  16.770898  1.4543670   140.18499  4.827784 2.800461e-02
ctDNA.DynamicsPersistently Positive 5.277803 1.7038744 195.939019 16.5092118 28090.67912 21.337835 3.850563e-06

Likelihood ratio test=23.80561 on 3 df, p=2.742645e-05, n=27
Wald test = 12.33767 on 3 df, p = 0.006311568

Covariance-Matrix:
                                    ctDNA.DynamicsConverted Negative ctDNA.DynamicsConverted Positive ctDNA.DynamicsPersistently Positive
ctDNA.DynamicsConverted Negative                           0.5653059                        0.2539661                           0.2567316
ctDNA.DynamicsConverted Positive                           0.2539661                        1.3769549                           0.5559755
ctDNA.DynamicsPersistently Positive                        0.2567316                        0.5559755                           2.9031880

#Association of ctDNA post-NAC and TRG status

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")

#Vertical Fisher plot for ctDNA clearance post-NAT and Rec Status
circ_data <- circ_data[!is.na(circ_data$ctDNA.postnac),]
circ_data$ctDNA.postnac <- factor(circ_data$ctDNA.postnac, levels=c("NEGATIVE","POSITIVE"))
circ_data$TRG <- factor(circ_data$TRG, levels = c("1", "2", "3"), labels = c("TRG1", "TRG2", "TRG3"))
contingency_table <- table(circ_data$TRG, circ_data$ctDNA.postnac)
fisher_exact_test <- fisher.test(contingency_table)
chi_square_test <- chisq.test(contingency_table)
Warning: Chi-squared approximation may be incorrect
print(chi_square_test)

    Pearson's Chi-squared test

data:  contingency_table
X-squared = 13.028, df = 2, p-value = 0.001483
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.001404
alternative hypothesis: two.sided
print(contingency_table)
      
       NEGATIVE POSITIVE
  TRG1        3        8
  TRG2       12        1
  TRG3        3        0
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2

# Swapping x and y in ggplot function to make bar plot vertical
ggplot(table_df, aes(y = Var1, x = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(x = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA post-NAT & TRG", y = "TRG Status", x = "Patients (%)", fill = "ctDNA post-NAC") +
  scale_x_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("NEGATIVE" = "lightblue3", "POSITIVE" = "red")) + # define custom colors
  theme(axis.text.y = element_text(angle = 0, hjust = 1.5, size = 14), # increase y-axis text size
        axis.text.x = element_text(size = 14, color = "black"), # increase x-axis text size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Recurrence label size

#RFS by ctDNA post-NAC and TRG - 3 groups

setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.postnac!="",]

circ_data$ctDNA.pCR <- NA
circ_data <- circ_data %>%
  mutate(ctDNA.pCR = case_when(
    ctDNA.postnac == "NEGATIVE" & TRG %in% c(1, 2) ~ "1",
    ctDNA.postnac == "NEGATIVE" & TRG == 3 ~ "2",
    ctDNA.postnac == "POSITIVE" & TRG %in% c(1, 2) ~ "3"
  ))

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.pCR, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.pCR, data = circ_data)

             n events median 0.95LCL 0.95UCL
ctDNA.pCR=1 15      4     NA      NA      NA
ctDNA.pCR=2  3      1     NA    5.65      NA
ctDNA.pCR=3  9      7   7.85    3.88      NA
circ_data <- circ_data[!is.na(circ_data$ctDNA.pCR),]
circ_data$ctDNA.pCR <- factor(circ_data$ctDNA.pCR, levels=c("1","2", "3"), labels=c("TRG1/2 ctDNA (-)","TRG3 ctDNA (-)", "TRG1/2 ctDNA (+)"))
survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.pCR, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.pCR, data = circ_data)

                            n events median 0.95LCL 0.95UCL
ctDNA.pCR=TRG1/2 ctDNA (-) 15      4     NA      NA      NA
ctDNA.pCR=TRG3 ctDNA (-)    3      1     NA    5.65      NA
ctDNA.pCR=TRG1/2 ctDNA (+)  9      7   7.85    3.88      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.pCR, data = circ_data,conf.int=0.95,conf.type="log-log")
summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.pCR, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.pCR=TRG1/2 ctDNA (-) 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     15       0    1.000   0.000        1.000        1.000
   24      7       4    0.709   0.124        0.395        0.881

                ctDNA.pCR=TRG3 ctDNA (-) 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            3            0            1            0            1            1 

                ctDNA.pCR=TRG1/2 ctDNA (+) 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            9            0            1            0            1            1 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, 
           break.time.by=6, palette=c("blue", "green", "red"), title="DFS - ctDNA post-NAC & TRG", ylab= "Recurrence-Free Survival", xlab="Months from Surgery", 
           legend.labs=c("TRG1/2 ctDNA (-)","TRG3 ctDNA (-)", "TRG1/2 ctDNA (+)"), legend.title="")


rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.postnac!="",]

circ_data$ctDNA.pCR <- NA
circ_data <- circ_data %>%
  mutate(ctDNA.pCR = case_when(
    ctDNA.postnac == "NEGATIVE" & TRG %in% c(1, 2) ~ "1",
    ctDNA.postnac == "NEGATIVE" & TRG == 3 ~ "2",
    ctDNA.postnac == "POSITIVE" & TRG %in% c(1, 2) ~ "3"
  ))
circ_data$ctDNA.pCR <- factor(circ_data$ctDNA.pCR, levels=c("1","2","3"))
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
cox_fit <- coxph(surv_object ~ ctDNA.pCR, data=circ_data)
summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.pCR, data = circ_data)

  n= 27, number of events= 12 

             coef exp(coef) se(coef)     z Pr(>|z|)  
ctDNA.pCR2 0.4458    1.5617   1.1200 0.398   0.6906  
ctDNA.pCR3 1.5927    4.9168   0.6308 2.525   0.0116 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.pCR2     1.562     0.6403    0.1739     14.03
ctDNA.pCR3     4.917     0.2034    1.4281     16.93

Concordance= 0.715  (se = 0.064 )
Likelihood ratio test= 6.72  on 2 df,   p=0.03
Wald test            = 6.67  on 2 df,   p=0.04
Score (logrank) test = 8.04  on 2 df,   p=0.02

#RFS by ctDNA post-NAC and pCR - 3 groups

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.4w!="",]
circ_data$RFS.months=circ_data$RFS.months-1
circ_data <- circ_data[circ_data$RFS.months>=0,]

circ_data$ctDNA.pCR <- NA
circ_data <- circ_data %>%
  mutate(ctDNA.pCR = case_when(
    ctDNA.postnac == "NEGATIVE" & pCR == "TRUE" ~ "1",
    ctDNA.postnac == "NEGATIVE" & pCR == "FALSE" ~ "2",
    ctDNA.postnac == "POSITIVE" & pCR == "FALSE" ~ "3",
  ))

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.pCR, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.pCR, data = circ_data)

   1 observation deleted due to missingness 
             n events median 0.95LCL 0.95UCL
ctDNA.pCR=1  4      1     NA    4.65      NA
ctDNA.pCR=2 14      4     NA   18.58      NA
ctDNA.pCR=3  9      7   6.85    2.88      NA
circ_data <- circ_data[!is.na(circ_data$ctDNA.pCR),]
circ_data$ctDNA.pCR <- factor(circ_data$ctDNA.pCR, levels=c("1","2", "3"), labels=c("pCR ctDNA (-)","No pCR ctDNA (-)", "No pCR ctDNA (+)"))
survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.pCR, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.pCR, data = circ_data)

                            n events median 0.95LCL 0.95UCL
ctDNA.pCR=pCR ctDNA (-)     4      1     NA    4.65      NA
ctDNA.pCR=No pCR ctDNA (-) 14      4     NA   18.58      NA
ctDNA.pCR=No pCR ctDNA (+)  9      7   6.85    2.88      NA
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.pCR, data = circ_data,conf.int=0.95,conf.type="log-log")
summary(KM_curve, times= c(0, 24))
Call: survfit(formula = surv_object ~ ctDNA.pCR, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.pCR=pCR ctDNA (-) 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0      4       0     1.00   0.000        1.000        1.000
   24      1       1     0.75   0.217        0.128        0.961

                ctDNA.pCR=No pCR ctDNA (-) 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     14       0    1.000   0.000        1.000         1.00
   24      5       4    0.686   0.132        0.359         0.87

                ctDNA.pCR=No pCR ctDNA (+) 
        time       n.risk      n.event     survival      std.err lower 95% CI upper 95% CI 
           0            9            0            1            0            1            1 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, 
           break.time.by=6, palette=c("blue", "green", "red"), title="DFS - ctDNA post-NAC & pCR", ylab= "Recurrence-Free Survival", xlab="Time (Months)", 
           legend.labs=c("pCR ctDNA (-)","No pCR ctDNA (-)", "No pCR ctDNA (+)"), legend.title="")


rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.4w!="",]
circ_data$RFS.months=circ_data$RFS.months-1
circ_data <- circ_data[circ_data$RFS.months>=0,]

circ_data$ctDNA.pCR <- NA
circ_data <- circ_data %>%
  mutate(ctDNA.pCR = case_when(
    ctDNA.postnac == "NEGATIVE" & pCR == "TRUE" ~ "1",
    ctDNA.postnac == "NEGATIVE" & pCR == "FALSE" ~ "2",
    ctDNA.postnac == "POSITIVE" & pCR == "FALSE" ~ "3",
  ))
circ_data$ctDNA.pCR <- factor(circ_data$ctDNA.pCR, levels=c("1","2","3"))
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
cox_fit <- coxph(surv_object ~ ctDNA.pCR, data=circ_data)
summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.pCR, data = circ_data)

  n= 27, number of events= 12 
   (1 observation deleted due to missingness)

              coef exp(coef) se(coef)     z Pr(>|z|)
ctDNA.pCR2 0.03296   1.03351  1.11864 0.029    0.976
ctDNA.pCR3 1.54364   4.68162  1.07082 1.442    0.149

           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.pCR2     1.034     0.9676    0.1154     9.258
ctDNA.pCR3     4.682     0.2136    0.5740    38.184

Concordance= 0.692  (se = 0.078 )
Likelihood ratio test= 6.58  on 2 df,   p=0.04
Wald test            = 6.63  on 2 df,   p=0.04
Score (logrank) test = 7.95  on 2 df,   p=0.02

#Multivariate cox regression for RFS - ctDNA post-NAC

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.postnac!="",]
circ_datadf <- as.data.frame(circ_data)

circ_data$ctDNA.postnac <- factor(circ_data$ctDNA.postnac, levels=c("NEGATIVE","POSITIVE"), labels = c("Negative", "Positive"))
circ_data$Age.Group <- factor(circ_data$Age.Group, levels=c("1","2"), labels = c("≤70", ">70"))
circ_data$Stage <- factor(circ_data$Stage, levels = c("I-II", "III-IV"))
surv_object <- Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) 
cox_fit <- coxph(surv_object ~ ctDNA.postnac + Age.Group + Stage, data=circ_data) 
ggforest(cox_fit, data = circ_data, main = "Multivariate Regression Model for RFS", refLabel = "Reference Group")

test.ph <- cox.zph(cox_fit)

#Multivariate cox regression for RFS - ctDNA week 4

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.4w!="",]

circ_data$ctDNA.4w <- factor(circ_data$ctDNA.4w, levels=c("NEGATIVE","POSITIVE"), labels = c("Negative", "Positive"))
circ_data$Age.Group <- factor(circ_data$Age.Group, levels=c("1","2"), labels = c("≤70", ">70"))
circ_data$Stage <- factor(circ_data$Stage, levels = c("I-II", "III-IV"))
surv_object <- Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) 
cox_fit <- coxph(surv_object ~ ctDNA.4w + Age.Group + Stage, data=circ_data) 
ggforest(cox_fit, data = circ_data, main = "Multivariate Regression Model for RFS", refLabel = "Reference Group")

test.ph <- cox.zph(cox_fit)

#Multivariate cox regression for RFS - ctDNA MRD Window

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("NCC ESCC Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]

circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"), labels = c("Negative", "Positive"))
circ_data$Age.Group <- factor(circ_data$Age.Group, levels=c("1","2"), labels = c("≤70", ">70"))
circ_data$Stage <- factor(circ_data$Stage, levels = c("I-II", "III-IV"))
surv_object <- Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) 
cox_fit <- coxph(surv_object ~ ctDNA.MRD + Age.Group + Stage, data=circ_data) 
ggforest(cox_fit, data = circ_data, main = "Multivariate Regression Model for RFS", refLabel = "Reference Group")

test.ph <- cox.zph(cox_fit)
LS0tCnRpdGxlOiAiTkNDIEVTQ0NfTmF0ZXJhIGFuYWx5c2lzIDA0MjYyMDI0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCmxpYnJhcnkoc3dpbXBsb3QpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShndGFibGUpCmxpYnJhcnkocmVhZHIpIApsaWJyYXJ5KG1vc2FpYykKbGlicmFyeShkcGx5cikgCmxpYnJhcnkoc3Vydml2YWwpIApsaWJyYXJ5KHN1cnZtaW5lcikgCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoY294cGhmKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShndHN1bW1hcnkpCmxpYnJhcnkoZmxleHRhYmxlKQpsaWJyYXJ5KHBhcmFtZXRlcnMpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoc3Vydml2YWwpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShEVCkKCiNjdEROQSBwb3NpdGl2aXR5IGJ5IHN0YWdlIGFuZCB3aW5kb3cKYGBge3J9CiNOdW1iZXIgb2YgUHRzIGF0IEJhc2VsaW5lIC0gcGVyY2VudGFnZSBwb3NpdGl2aXR5IGJ5IHN0YWdlCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLnByZW5hYyE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnRvdGFsX2Jhc2UgPC0gc3VtKCFpcy5uYShjaXJjX2RhdGEkY3RETkEucHJlbmFjKSkKcHJpbnQodG90YWxfYmFzZSkKY2lyY19kYXRhJGN0RE5BLnByZW5hYyA8LSBhcy5mYWN0b3IoY2lyY19kYXRhJGN0RE5BLnByZW5hYykKY29udF90YWJsZV9iYXNlIDwtIHRhYmxlKGNpcmNfZGF0YSRjU3RhZ2UsIGNpcmNfZGF0YSRjdEROQS5wcmVuYWMpCnByaW50KGNvbnRfdGFibGVfYmFzZSkKCiNOdW1iZXIgb2YgUHRzIHBvc3QgTkFUIC0gcGVyY2VudGFnZSBwb3NpdGl2aXR5IGJ5IHN0YWdlCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLnBvc3RuYWMhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgp0b3RhbF9wb3N0bmF0IDwtIHN1bSghaXMubmEoY2lyY19kYXRhJGN0RE5BLnBvc3RuYWMpKQpwcmludCh0b3RhbF9wb3N0bmF0KQpjaXJjX2RhdGEkY3RETkEucG9zdG5hYyA8LSBhcy5mYWN0b3IoY2lyY19kYXRhJGN0RE5BLnBvc3RuYWMpCmNvbnRfdGFibGVfcG9zdG5hdCA8LSB0YWJsZShjaXJjX2RhdGEkY1N0YWdlLCBjaXJjX2RhdGEkY3RETkEucG9zdG5hYykKcHJpbnQoY29udF90YWJsZV9wb3N0bmF0KQoKI051bWJlciBvZiBQdHMgcG9zdC1zdXJnZXJ5IC0gcGVyY2VudGFnZSBwb3NpdGl2aXR5IGJ5IHN0YWdlCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLjR3IT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKdG90YWxfbXJkIDwtIHN1bSghaXMubmEoY2lyY19kYXRhJGN0RE5BLjR3KSkKcHJpbnQodG90YWxfbXJkKQpjaXJjX2RhdGEkY3RETkEuNHcgPC0gYXMuZmFjdG9yKGNpcmNfZGF0YSRjdEROQS40dykKY29udF90YWJsZV9tcmQgPC0gdGFibGUoY2lyY19kYXRhJGNTdGFnZSwgY2lyY19kYXRhJGN0RE5BLjR3KQpwcmludChjb250X3RhYmxlX21yZCkKYGBgCgojU3VtbWFyeSBUYWJsZQpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKY2lyY19kYXRhX3N1YnNldCA8LSBjaXJjX2RhdGEgJT4lCiAgc2VsZWN0KAogICAgR2VuZGVyLAogICAgQWdlLAogICAgUFMsCiAgICBjU3RhZ2UsCiAgICBUTk0uRGV0YWlsZWQsCiAgICBOZW8uUmVnaW1lbnQsCiAgICB5cFN0YWdlLAogICAgeXBUTk0uRGV0YWlsZWQsCiAgICBUUkcsCiAgICBwQ1IsCiAgICBBZGouUmVnaW1lbiwKICAgIFJGUy5FdmVudCwKICAgIE9TLkV2ZW50LAogICAgUkZTLm1vbnRocywKICAgIE9TLm1vbnRocykgJT4lCiAgbXV0YXRlKAogICAgR2VuZGVyID0gZmFjdG9yKEdlbmRlciksCiAgICBBZ2UgPSBhcy5udW1lcmljKEFnZSksCiAgICBQUyA9IGZhY3RvcihQUyksCiAgICBjU3RhZ2UgPSBmYWN0b3IoY1N0YWdlKSwKICAgIFROTS5EZXRhaWxlZCA9IGZhY3RvcihUTk0uRGV0YWlsZWQpLAogICAgTmVvLlJlZ2ltZW50ID0gZmFjdG9yKE5lby5SZWdpbWVudCksCiAgICB5cFN0YWdlID0gZmFjdG9yKHlwU3RhZ2UpLAogICAgeXBUTk0uRGV0YWlsZWQgPSBmYWN0b3IoeXBUTk0uRGV0YWlsZWQpLAogICAgVFJHID0gZmFjdG9yKFRSRyksCiAgICBwQ1IgPSBmYWN0b3IocENSKSwKICAgIEFkai5SZWdpbWVuID0gZmFjdG9yKEFkai5SZWdpbWVuKSwKICAgIFJGUy5FdmVudCA9IGZhY3RvcihSRlMuRXZlbnQsIGxldmVscyA9IGMoIkZBTFNFIiwgIlRSVUUiKSwgbGFiZWxzID0gYygiTm8gUmVjdXJyZW5jZSIsICJSZWN1cnJlbmNlIikpLAogICAgT1MuRXZlbnQgPSBmYWN0b3IoT1MuRXZlbnQsIGxldmVscyA9IGMoIkZBTFNFIiwgIlRSVUUiKSwgbGFiZWxzID0gYygiQWxpdmUiLCAiRGVjZWFzZWQiKSksCiAgICBSRlMubW9udGhzID0gYXMubnVtZXJpYyhSRlMubW9udGhzKSwKICAgIE9TLm1vbnRocyA9IGFzLm51bWVyaWMoT1MubW9udGhzKSkKdGFibGUxIDwtIGNpcmNfZGF0YV9zdWJzZXQgJT4lCiAgdGJsX3N1bW1hcnkoCiAgICBzdGF0aXN0aWMgPSBsaXN0KAogICAgICBhbGxfY29udGludW91cygpIH4gInttZWRpYW59ICh7bWlufSAtIHttYXh9KSIsCiAgICAgIGFsbF9jYXRlZ29yaWNhbCgpIH4gIntufSAoe3B9JSkiKSkgJT4lCiAgYm9sZF9sYWJlbHMoKQp0YWJsZTEKZml0MSA8LSBhc19mbGV4X3RhYmxlKAogIHRhYmxlMSwKICBpbmNsdWRlID0gZXZlcnl0aGluZygpLAogIHJldHVybl9jYWxscyA9IEZBTFNFLAogIHN0cmlwX21kX2JvbGQgPSBUUlVFKQpmaXQxCnNhdmVfYXNfZG9jeChmaXQxLCBwYXRoPSAifi9Eb3dubG9hZHMvdGFibGUxLmRvY3giKQpgYGAKCgoKI0hlYXRtYXAgZm9yIHRoZSBjbGluaWNhbCBmYWN0b3JzCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiTkNDIEVTQ0MgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKY2lyY19kYXRhIDwtIGNpcmNfZGF0YSAlPiUgYXJyYW5nZShjU3RhZ2UpCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKaGEgPC0gSGVhdG1hcEFubm90YXRpb24oCiAgY1N0YWdlID0gY2lyY19kYXRhJGNTdGFnZSwKICBHZW5kZXIgPSBjaXJjX2RhdGEkR2VuZGVyLAogIE5lby5SZWdpbWVudCA9IGNpcmNfZGF0YSROZW8uUmVnaW1lbnQsCiAgVFJHID0gY2lyY19kYXRhJFRSRywKICBwQ1IgPSBjaXJjX2RhdGEkcENSLAogIHlwU3RhZ2UgPSBjaXJjX2RhdGEkeXBTdGFnZSwKICBBZGouUmVnaW1lbiA9IGNpcmNfZGF0YSRBZGouUmVnaW1lbiwKICBjdEROQS5wcmVuYWMgPSBjaXJjX2RhdGEkY3RETkEucHJlbmFjLAogIGN0RE5BLnBvc3RuYWMgPSBjaXJjX2RhdGEkY3RETkEucG9zdG5hYywKICBjdEROQS40dyA9IGNpcmNfZGF0YSRjdEROQS40dywKICBjdEROQS4xMncgPSBjaXJjX2RhdGEkY3RETkEuMTJ3LAogIGN0RE5BLjI0dyA9IGNpcmNfZGF0YSRjdEROQS4yNHcsCiAgY3RETkEuMzZ3ID0gY2lyY19kYXRhJGN0RE5BLjM2dywKICBjdEROQS40OHcgPSBjaXJjX2RhdGEkY3RETkEuNDh3LAogIGN0RE5BLnBvc3RNUkQgPSBjaXJjX2RhdGEkY3RETkEucG9zdE1SRCwKICBSRlMuRXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50LAogIE9TLkV2ZW50ID0gY2lyY19kYXRhJE9TLkV2ZW50LAogIAogICAgY29sID0gbGlzdChjU3RhZ2UgPSBjKCJJIiA9ICJzZWFncmVlbjEiLCAiSUkiID0gImtoYWtpIiwgIklJSSIgPSAib3JhbmdlIiwgIklWIiA9ICJkYXJrbWFnZW50YSIpLAogICAgR2VuZGVyID0gYygiRkVNQUxFIiA9ICJnb2xkZW5yb2QiICwgIk1BTEUiID0gImJsdWU0IiksCiAgICBOZW8uUmVnaW1lbnQgPSBjKCJGT0xGT1g2IiA9ICJjb3JhbCIsICJGUCIgPSJkYXJrZ3JlZW4iLCAiRENGIiA9ImJyb3duIiksCiAgICBUUkcgPSBjKCIxIiA9ICJ5ZWxsb3czIiwgIjIiID0iZGFya2dyZWVuIiwgIjMiID0gImJyb3duMiIpLAogICAgcENSID0gYygiVFJVRSIgPSAibGlnaHRibHVlIiwgIkZBTFNFIiA9Im9yYW5nZSIpLAogICAgeXBTdGFnZSA9IGMoIjAiID0gImtoYWtpIiwiSSIgPSAic2VhZ3JlZW4yIiwgIklJIiA9ICJjb3JuZmxvd2VyYmx1ZSIsIklJSSIgPSAib3JhbmdlIiwiSVYiID0iZGFya21hZ2VudGEiKSwKICAgIEFkai5SZWdpbWVuID0gYygiTml2b2x1bWFiIiA9ICJkYXJrbWFnZW50YSIpLAogICAgY3RETkEucHJlbmFjID0gYygiUE9TSVRJVkUiID0gInJlZDMiLCAiTkVHQVRJVkUiID0iYmx1ZSIpLAogICAgY3RETkEucG9zdG5hYyA9IGMoIlBPU0lUSVZFIiA9ICJyZWQzIiwgIk5FR0FUSVZFIiA9ImJsdWUiKSwKICAgIGN0RE5BLjR3ID0gYygiUE9TSVRJVkUiID0gInJlZDMiLCAiTkVHQVRJVkUiID0iYmx1ZSIpLAogICAgY3RETkEuMTJ3ID0gYygiUE9TSVRJVkUiID0gInJlZDMiLCAiTkVHQVRJVkUiID0iYmx1ZSIpLAogICAgY3RETkEuMjR3ID0gYygiUE9TSVRJVkUiID0gInJlZDMiLCAiTkVHQVRJVkUiID0iYmx1ZSIpLAogICAgY3RETkEuMzZ3ID0gYygiUE9TSVRJVkUiID0gInJlZDMiLCAiTkVHQVRJVkUiID0iYmx1ZSIpLAogICAgY3RETkEuNDh3ID0gYygiUE9TSVRJVkUiID0gInJlZDMiLCAiTkVHQVRJVkUiID0iYmx1ZSIpLAogICAgY3RETkEucG9zdE1SRCA9IGMoIlBPU0lUSVZFIiA9ICJyZWQzIiwgIk5FR0FUSVZFIiA9ImJsdWUiKSwKICAgIFJGUy5FdmVudCA9IGMoIlRSVUUiID0gInJlZDMiLCAiRkFMU0UiID0iYmx1ZSIpLAogICAgT1MuRXZlbnQgPSBjKCJUUlVFIiA9ICJibGFjayIsICJGQUxTRSIgPSJncmV5IikKKQopCmh0IDwtIEhlYXRtYXAobWF0cml4KG5yb3cgPSAwLCBuY29sID0gbGVuZ3RoKGNpcmNfZGF0YSRjU3RhZ2UpKSxzaG93X3Jvd19uYW1lcyA9IEZBTFNFLGNsdXN0ZXJfcm93cyA9IEYsY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsIHRvcF9hbm5vdGF0aW9uID0gaGEpCnBkZigiaGVhdG1hcC5wZGYiLHdpZHRoID0gNywgaGVpZ2h0ID0gNykKZHJhdyhodCwgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJib3R0b20iKQpkZXYub2ZmKCkKYGBgCgojT3ZlcnZpZXcgcGxvdApgYGB7cn0Kc2V0d2QoIn4vRG93bmxvYWRzIikKY2xpbnN0YWdlPC0gcmVhZC5jc3YoIk5DQ19FU0NDX09QLmNzdiIpCmNsaW5zdGFnZV9kZjwtIGFzLmRhdGEuZnJhbWUoY2xpbnN0YWdlKQoKI0Rpc3BsYXkgdGhlIHN3aW1tZXIgcGxvdCB3aXRoIHRoZSBsYWJlbCBib3gKb3Bsb3Q8LXN3aW1tZXJfcGxvdChkZj1jbGluc3RhZ2VfZGYsCiAgICAgICAgICAgICAgICAgICAgaWQ9J1BhdGllbnROYW1lJywKICAgICAgICAgICAgICAgICAgICBlbmQ9J2Z1LmRpZmYubW9udGhzJywKICAgICAgICAgICAgICAgICAgICBmaWxsPSdncmF5JywKICAgICAgICAgICAgICAgICAgICB3aWR0aD0uMDEsKQpvcGxvdCA8LSBvcGxvdCArIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkKb3Bsb3QgPC0gb3Bsb3QgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKC0xMiwgMTA4LCBieSA9IDYpKQpvcGxvdCA8LSBvcGxvdCArIGxhYnMoeCA9IlBhdGllbnRzIiAsIHk9Ik1vbnRocyBmcm9tIFN1cmdlcnkiKQpvcGxvdAoKIyNwbG90IGV2ZW50cwpvcGxvdF9ldjEgPC0gb3Bsb3QgKyBzd2ltbWVyX3BvaW50cyhkZl9wb2ludHM9Y2xpbnN0YWdlX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0nUGF0aWVudE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lPSdkYXRlLmRpZmYubW9udGhzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9zaGFwZSA9J0V2ZW50X3R5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX2NvbCA9ICdFdmVudCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9My41LGZpbGw9J2JsYWNrJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2NvbD0nZGFya2dyZWVuJwopCm9wbG90X2V2MQoKI1NoYXBlIGN1c3RvbWl6YXRpb24gdG8gRXZlbnRfdHlwZQoKb3Bsb3RfZXYxLjEgPC0gb3Bsb3RfZXYxICsgZ2dwbG90Mjo6c2NhbGVfc2hhcGVfbWFudWFsKG5hbWU9IkV2ZW50X3R5cGUiLHZhbHVlcz1jKDEsMTYsNiwxOCwxOCw0KSxicmVha3M9YygnY3RETkFfbmVnJywnY3RETkFfcG9zJywgJ0ltYWdpbmcnLCdTdXJnZXJ5JywnQmlvcHN5JywgJ0RlYXRoJykpCgpvcGxvdF9ldjEuMQoKI3Bsb3QgdHJlYXRtZW50CgpvcGxvdF9ldjIgPC0gb3Bsb3RfZXYxLjEgKyBzd2ltbWVyX2xpbmVzKGRmX2xpbmVzPWNsaW5zdGFnZV9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0nUGF0aWVudE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0PSdUeF9zdGFydC5tb250aHMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD0nVHhfZW5kLm1vbnRocycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9jb2w9J1R4X3R5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9My41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVfYWxwaGEgPSAxLjApCm9wbG90X2V2MiA8LSBvcGxvdF9ldjIgKyBndWlkZXMobGluZXR5cGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNSwgY29sb3IgPSAiYmxhY2siKSkpCm9wbG90X2V2MiAgCgoKI2NvbG91ciBjdXN0b21pemF0aW9uCiNvcmFuZ2U9TkFDIGNoZW1vLG9yYW5nZXJlZD1OQUMgQ2hlbW9JTywgIHB1cnBsZT1BQ1QsIEJsYWNrPURlYXRoLCBSZWQ9UEQsIGN0RE5BIG5lZ2F0aXZlPXdoaXRlLCBjdEROQSBwb3NpdGl2ZT1ibGFjaywgY3lzdGVjdG9teT1ibHVlLCBUVVJCVD1ncmF5LCBBQ1QgQ2hlbW89cHVycGxlLCBBQ1QgSU89bGlnaHRibHVlIApvcGxvdF9ldjIuMiA8LSBvcGxvdF9ldjIgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iRXZlbnQiLHZhbHVlcz1jKCJsaWdodGJsdWUiLCAiZ3JheSIsICJibGFjayIsICJibGFjayIsICJvcmFuZ2UiLCAiZ3JlZW4iLCAicmVkIiwgImJsdWUiKSkKb3Bsb3RfZXYyLjIKYGBgCgojUkZTIGJ5IGN0RE5BIHBvc3QtTkFUCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEucG9zdG5hYyE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCl+Y3RETkEucG9zdG5hYywgZGF0YSA9IGNpcmNfZGF0YSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUkZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50KQpLTV9jdXJ2ZSA8LSBzdXJ2Zml0KHN1cnZfb2JqZWN0IH4gY3RETkEucG9zdG5hYywgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUkZTIC0gY3RETkEgcG9zdC1OQUMiLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gU3VyZ2VyeSIsIGxlZ2VuZC5sYWJzPWMoImN0RE5BIE5lZ2F0aXZlIiwgImN0RE5BIFBvc2l0aXZlIiksIGxlZ2VuZC50aXRsZT0iIikKc3VtbWFyeShLTV9jdXJ2ZSwgdGltZXM9IGMoMTIsIDI0KSkKY2lyY19kYXRhJGN0RE5BLnBvc3RuYWMgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5wb3N0bmFjLCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEucG9zdG5hYywgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQpgYGAKCgojUkZTIGJ5IGN0RE5BIENsZWFyYW5jZSBwb3N0LU5BQyAtIDMgR3JvdXBzCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiTkNDIEVTQ0MgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5DbGVhcmFuY2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLkNsZWFyYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUkZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50KQpLTV9jdXJ2ZSA8LSBzdXJ2Zml0KHN1cnZfb2JqZWN0IH4gY3RETkEuQ2xlYXJhbmNlLCBkYXRhID0gY2lyY19kYXRhLGNvbmYuaW50PTAuOTUsY29uZi50eXBlPSJsb2ctbG9nIikgCmdnc3VydnBsb3QoS01fY3VydmUsIGRhdGEgPSBjaXJjX2RhdGEsIHB2YWwgPSBGQUxTRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJyZWQiLCJibHVlIiwiZ3JlZW4iKSwgdGl0bGU9IlJGUyAtIGN0RE5BIENsZWFyYW5jZSBwb3N0LU5BQyIsIHlsYWI9ICJSZWN1cnJlbmNlLUZyZWUgU3Vydml2YWwiLCB4bGFiPSJNb250aHMgZnJvbSBTdXJnZXJ5IiwgbGVnZW5kLmxhYnM9YygiTm8gQ2xlYXJhbmNlIiwgIlN1c3RhaW5lZCIsICJUcmFuc2llbnQiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygxMiwgMTgsIDI0KSkKY2lyY19kYXRhJGN0RE5BLkNsZWFyYW5jZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLkNsZWFyYW5jZSwgbGV2ZWxzPWMoIlN1c3RhaW5lZCIsIlRyYW5zaWVudCIsICJObyBDbGVhcmFuY2UiKSkKY294X2ZpdCA8LSBjb3hwaChzdXJ2X29iamVjdCB+IGN0RE5BLkNsZWFyYW5jZSwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmBgYAoKCiNSRlMgYnkgY3RETkEgYXQgNCB3ZWVrcwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLjR3IT0iIixdCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLjR3LCBkYXRhID0gY2lyY19kYXRhKQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS40dywgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUkZTIC0gY3RETkEgYXQgd2VlayA0IiwgeWxhYj0gIlJlY3VycmVuY2UtRnJlZSBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIFN1cmdlcnkiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCmNpcmNfZGF0YSRjdEROQS40dyA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLjR3LCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAyNCkpCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS40dywgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQpgYGAKCgojUkZTIGJ5IGN0RE5BIE1SRCBXaW5kb3cgKDItMTYgd2Vla3MgcG9zdC1vcCkKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiTkNDIEVTQ0MgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5NUkQhPSIiLF0KCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCl+Y3RETkEuTVJELCBkYXRhID0gY2lyY19kYXRhKQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlJGUyAtIGN0RE5BIE1SRCBXaW5kb3ciLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gU3VyZ2VyeSIsIGxlZ2VuZC5sYWJzPWMoImN0RE5BIE5lZ2F0aXZlIiwgImN0RE5BIFBvc2l0aXZlIiksIGxlZ2VuZC50aXRsZT0iIikKY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKc3VtbWFyeShLTV9jdXJ2ZSwgdGltZXM9IGMoMCwgMjQpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCmBgYAoKCiNSRlMgYnkgY3RETkEgcG9zdCBNUkQKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiTkNDIEVTQ0MgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5wb3N0TVJEIT0iIixdCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLnBvc3RNUkQsIGRhdGEgPSBjaXJjX2RhdGEpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLnBvc3RNUkQsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlJGUyAtIGN0RE5BIFN1cnZlaWxsYW5jZSAocG9zdC0pTVJEIiwgeWxhYj0gIlJlY3VycmVuY2UtRnJlZSBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIFN1cmdlcnkiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCmNpcmNfZGF0YSRjdEROQS5wb3N0TVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEucG9zdE1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKc3VtbWFyeShLTV9jdXJ2ZSwgdGltZXM9IGMoMCwgMjQpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEucG9zdE1SRCwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQpgYGAKCgojVGltZS1kZXBlbmRlbnQgYW5hbHlzaXMgZm9yIHBvc3QtTVJEICg+MTYgd2Vla3MpIHRpbWUgcG9pbnRzCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmR0IDwtIHJlYWRfeGxzeCgiTkNDIEVTQ0MgVGltZSBEZXBlbmRlbnQgRGF0YV9wb3N0TVJELnhsc3giKSB8PgogIGNsZWFuX25hbWVzKCkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gYyh3aW5kb3dfc3RhcnRfZGF0ZSxkZnNfZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKGFzLkRhdGUoLngsIGZvcm1hdCA9ICIlWS0lbS0lZCIpKSkpCgpkdF9iaW9tYXJrZXIgPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX3N0YXR1czpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBmaWx0ZXIoY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUpIHw+CiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdXJ2ZWlsbGFuY2VfMV9zdGF0dXM6c3VydmVpbGxhbmNlXzEyX2RhdGUsCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gYygidmlzaXRfbnVtYmVyIiwgIi52YWx1ZSIpLAogICAgICAgICAgICAgICBuYW1lc19wYXR0ZXJuID0gInN1cnZlaWxsYW5jZV8oLilfKC4qKSIpIHw+CiAgbXV0YXRlKGJpb21hcmtlcl90aW1lID0gZGF5KGRheXMoZGF0ZSAtIHdpbmRvd19zdGFydF9kYXRlKSkpIHw+CiAgc2VsZWN0KHB0c19pZCwgYmlvbWFya2VyX3RpbWUsIGJpb21hcmtlcl9zdGF0dXMgPSBzdGF0dXMpIHw+CiAgZmlsdGVyKCFpcy5uYShiaW9tYXJrZXJfdGltZSkpCgpnbGltcHNlKGR0X2Jpb21hcmtlcikKCmR0X3N1cnZpdmFsIDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlOmRmc19kYXRlLCBkZnNfZXZlbnQpIHw+ICAjIEFkZGVkIGRmc19ldmVudCBoZXJlCiAgZmlsdGVyKGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlKSB8PgogIG11dGF0ZShkZnNfdGltZSA9IChkZnNfZGF0ZSAtIHdpbmRvd19zdGFydF9kYXRlKSwKICAgICAgICAgZGZzX3RpbWUgPSBkYXkoZGF5cyhkZnNfdGltZSkpLAogICAgICAgICBkZnNfZXZlbnQgPSBhcy5udW1lcmljKGRmc19ldmVudCkpIHw+CiAgc2VsZWN0KHB0c19pZCwgZGZzX3RpbWUsIGRmc19ldmVudCkKCmdsaW1wc2UoZHRfc3Vydml2YWwpCgphdXggPC0gZHRfc3Vydml2YWwgJT4lIAogIGZpbHRlcihkZnNfdGltZSA8PSAwKQoKdGFiIDwtIGxlZnRfam9pbihhdXgsIGR0KSB8PgogIHNlbGVjdChwdHNfaWQsIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfdGltZSwgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGRmc19kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoLngpKSkgfD4KICBzZWxlY3QocHRzX2lkLCB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX2RhdGUsIGRmc190aW1lKQoKZGF0YXRhYmxlKHRhYiwgZmlsdGVyID0gInRvcCIpCgpkdF9zdXJ2aXZhbCA8LSBkdF9zdXJ2aXZhbCB8PgogIGZpbHRlcihkZnNfdGltZSA+IDApCgphdXggPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IC54IC0gd2luZG93X3N0YXJ0X2RhdGUpKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IC54IDwgMCkpIHw+CiAgcm93d2lzZSgpIHw+CiAgbXV0YXRlKHN1bV9uZWcgPSAKICAgICAgICAgICBzdW0oY19hY3Jvc3Moc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSksCiAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpICB8PgogIHNlbGVjdChwdHNfaWQsIHN1bV9uZWcpCgp0YWIgPC0gbGVmdF9qb2luKGF1eCwgZHQpIHw+CiAgZmlsdGVyKHN1bV9uZWcgPiAwKSB8PgogIHNlbGVjdChwdHNfaWQsIHN1bV9uZWcsIHdpbmRvd19zdGFydF9kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSB3aW5kb3dfc3RhcnRfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIAoKZGF0YXRhYmxlKHRhYiwgZmlsdGVyID0gInRvcCIpCgphdXggPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiAueCAtIHdpbmRvd19zdGFydF9kYXRlKSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzJfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGRmc19kYXRlIDwgLngpKSB8PgogIHJvd3dpc2UoKSB8PgogIG11dGF0ZShuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCA9IHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMl9kYXRlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xMl9kYXRlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsCiAgICAgICAgICAgICAgICAuZm5zID0gfiAhaXMubmEoLngpKSkgfD4KICBtdXRhdGUodG90YWxfYmlvbWFya2VyID0gc3VtKGNfYWNyb3NzKHN1cnZlaWxsYW5jZV8yX2RhdGU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xMl9kYXRlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSB8PgogIHNlbGVjdChwdHNfaWQsIG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIpCgp0ZW1wIDwtIGF1eCB8PiAKICBzZWxlY3QoLXB0c19pZCkgfD4KICBncm91cF9ieShuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCwgdG90YWxfYmlvbWFya2VyKSB8PgogIHN1bW1hcml6ZShmcmVxID0gbigpKQoKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBzZWxlY3QocHRzX2lkLCBuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCwgdG90YWxfYmlvbWFya2VyLCAKICAgICAgICAgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8yX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGRmc19kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoLngpKSkgfD4KICBmaWx0ZXIobl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQgPiAwKQpkYXRhdGFibGUodGFiLCBmaWx0ZXIgPSAidG9wIikKCmF1eCA8LSB0bWVyZ2UoZGF0YTEgPSBkdF9zdXJ2aXZhbCwgCiAgICAgICAgICAgICAgZGF0YTIgPSBkdF9zdXJ2aXZhbCwKICAgICAgICAgICAgICBpZCA9IHB0c19pZCwgCiAgICAgICAgICAgICAgZGZzX2V2ZW50ID0gZXZlbnQoZGZzX3RpbWUsIGRmc19ldmVudCkpCmR0X2ZpbmFsIDwtIHRtZXJnZShkYXRhMSA9IGF1eCwgCiAgICAgICAgICAgICAgICAgICBkYXRhMiA9IGR0X2Jpb21hcmtlciwKICAgICAgICAgICAgICAgICAgIGlkID0gcHRzX2lkLCAKICAgICAgICAgICAgICAgICAgIGJpb21hcmtlcl9zdGF0dXMgPSAKICAgICAgICAgICAgICAgICAgICAgdGRjKGJpb21hcmtlcl90aW1lLCBiaW9tYXJrZXJfc3RhdHVzKSkKCmRhdGF0YWJsZShkdF9maW5hbCwgZmlsdGVyID0gInRvcCIpCgojIFN5bnRheCBpZiB0aGVyZSBpcyBub3QgdGltZS1kZXBlbmRlbnQgY292YXJpYXRlCiMgZml0IDwtIGNveHBoKFN1cnYoZGZzX3RpbWUsIGRmc19ldmVudCkgfiBiaW9tYXJrZXJfc3RhdHVzLAojICAgICAgICAgICAgICBkYXRhID0gZHRfZmluYWwpCiMgc3VtbWFyeShmaXQpCgpmaXQgPC0gY294cGgoU3Vydih0c3RhcnQsIHRzdG9wLCBkZnNfZXZlbnQpIH4gYmlvbWFya2VyX3N0YXR1cywKICAgICAgICAgICAgIGRhdGEgPSBkdF9maW5hbCkKc3VtbWFyeShmaXQpCmBgYAoKCiNSRlMgYnkgY3RETkEgYW55dGltZSBwb3N0LXN1cmdlcnkKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiTkNDIEVTQ0MgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hbnl0aW1lIT0iIixdCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLmFueXRpbWUsIGRhdGEgPSBjaXJjX2RhdGEpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLmFueXRpbWUsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlJGUyAtIGN0RE5BIGFueXRpbWUgcG9zdC1zdXJnZXJ5IiwgeWxhYj0gIlJlY3VycmVuY2UtRnJlZSBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIFN1cmdlcnkiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCmNpcmNfZGF0YSRjdEROQS5hbnl0aW1lIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuYW55dGltZSwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKc3VtbWFyeShLTV9jdXJ2ZSwgdGltZXM9IGMoMCwgMjQpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuYW55dGltZSwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQpgYGAKCgojVGltZS1kZXBlbmRlbnQgYW5hbHlzaXMgZm9yIHBvc3Qtc3VyZ2VyeSB0aW1lIHBvaW50cwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpkdCA8LSByZWFkX3hsc3goIk5DQyBFU0NDIFRpbWUgRGVwZW5kZW50IERhdGFfYW55dGltZSBwb3N0b3AueGxzeCIpIHw+CiAgY2xlYW5fbmFtZXMoKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBjKHdpbmRvd19zdGFydF9kYXRlLGRmc19kYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoYXMuRGF0ZSgueCwgZm9ybWF0ID0gIiVZLSVtLSVkIikpKSkKCmR0X2Jpb21hcmtlciA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfc3RhdHVzOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSB8PgogIGZpbHRlcihjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSkgfD4KICBwaXZvdF9sb25nZXIoY29scyA9IHN1cnZlaWxsYW5jZV8xX3N0YXR1czpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSBjKCJ2aXNpdF9udW1iZXIiLCAiLnZhbHVlIiksCiAgICAgICAgICAgICAgIG5hbWVzX3BhdHRlcm4gPSAic3VydmVpbGxhbmNlXyguKV8oLiopIikgfD4KICBtdXRhdGUoYmlvbWFya2VyX3RpbWUgPSBkYXkoZGF5cyhkYXRlIC0gd2luZG93X3N0YXJ0X2RhdGUpKSkgfD4KICBzZWxlY3QocHRzX2lkLCBiaW9tYXJrZXJfdGltZSwgYmlvbWFya2VyX3N0YXR1cyA9IHN0YXR1cykgfD4KICBmaWx0ZXIoIWlzLm5hKGJpb21hcmtlcl90aW1lKSkKCmdsaW1wc2UoZHRfYmlvbWFya2VyKQoKZHRfc3Vydml2YWwgPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGU6ZGZzX2RhdGUsIGRmc19ldmVudCkgfD4gICMgQWRkZWQgZGZzX2V2ZW50IGhlcmUKICBmaWx0ZXIoY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUpIHw+CiAgbXV0YXRlKGRmc190aW1lID0gKGRmc19kYXRlIC0gd2luZG93X3N0YXJ0X2RhdGUpLAogICAgICAgICBkZnNfdGltZSA9IGRheShkYXlzKGRmc190aW1lKSksCiAgICAgICAgIGRmc19ldmVudCA9IGFzLm51bWVyaWMoZGZzX2V2ZW50KSkgfD4KICBzZWxlY3QocHRzX2lkLCBkZnNfdGltZSwgZGZzX2V2ZW50KQoKZ2xpbXBzZShkdF9zdXJ2aXZhbCkKCmF1eCA8LSBkdF9zdXJ2aXZhbCAlPiUgCiAgZmlsdGVyKGRmc190aW1lIDw9IDApCgp0YWIgPC0gbGVmdF9qb2luKGF1eCwgZHQpIHw+CiAgc2VsZWN0KHB0c19pZCwgd2luZG93X3N0YXJ0X2RhdGUsIGRmc190aW1lLCBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gZGZzX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZSgueCkpKSB8PgogIHNlbGVjdChwdHNfaWQsIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwgZGZzX3RpbWUpCgpkYXRhdGFibGUodGFiLCBmaWx0ZXIgPSAidG9wIikKCmR0X3N1cnZpdmFsIDwtIGR0X3N1cnZpdmFsIHw+CiAgZmlsdGVyKGRmc190aW1lID4gMCkKCmF1eCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggLSB3aW5kb3dfc3RhcnRfZGF0ZSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggPCAwKSkgfD4KICByb3d3aXNlKCkgfD4KICBtdXRhdGUoc3VtX25lZyA9IAogICAgICAgICAgIHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSwKICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkgIHw+CiAgc2VsZWN0KHB0c19pZCwgc3VtX25lZykKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBmaWx0ZXIoc3VtX25lZyA+IDApIHw+CiAgc2VsZWN0KHB0c19pZCwgc3VtX25lZywgd2luZG93X3N0YXJ0X2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHdpbmRvd19zdGFydF9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoLngpKSkgCgpkYXRhdGFibGUodGFiLCBmaWx0ZXIgPSAidG9wIikKCmF1eCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGRmc19kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IC54IC0gd2luZG93X3N0YXJ0X2RhdGUpKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMl9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLAogICAgICAgICAgICAgICAgLmZucyA9IH4gZGZzX2RhdGUgPCAueCkpIHw+CiAgcm93d2lzZSgpIHw+CiAgbXV0YXRlKG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50ID0gc3VtKGNfYWNyb3NzKHN1cnZlaWxsYW5jZV8yX2RhdGU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VydmVpbGxhbmNlXzEyX2RhdGUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwKICAgICAgICAgICAgICAgIC5mbnMgPSB+ICFpcy5uYSgueCkpKSB8PgogIG11dGF0ZSh0b3RhbF9iaW9tYXJrZXIgPSBzdW0oY19hY3Jvc3Moc3VydmVpbGxhbmNlXzJfZGF0ZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VydmVpbGxhbmNlXzEyX2RhdGUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpIHw+CiAgc2VsZWN0KHB0c19pZCwgbl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQsIHRvdGFsX2Jpb21hcmtlcikKCnRlbXAgPC0gYXV4IHw+IAogIHNlbGVjdCgtcHRzX2lkKSB8PgogIGdyb3VwX2J5KG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIpIHw+CiAgc3VtbWFyaXplKGZyZXEgPSBuKCkpCgoKdGFiIDwtIGxlZnRfam9pbihhdXgsIGR0KSB8PgogIHNlbGVjdChwdHNfaWQsIG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIsIAogICAgICAgICBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzJfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gZGZzX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZSgueCkpKSB8PgogIGZpbHRlcihuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCA+IDApCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKYXV4IDwtIHRtZXJnZShkYXRhMSA9IGR0X3N1cnZpdmFsLCAKICAgICAgICAgICAgICBkYXRhMiA9IGR0X3N1cnZpdmFsLAogICAgICAgICAgICAgIGlkID0gcHRzX2lkLCAKICAgICAgICAgICAgICBkZnNfZXZlbnQgPSBldmVudChkZnNfdGltZSwgZGZzX2V2ZW50KSkKZHRfZmluYWwgPC0gdG1lcmdlKGRhdGExID0gYXV4LCAKICAgICAgICAgICAgICAgICAgIGRhdGEyID0gZHRfYmlvbWFya2VyLAogICAgICAgICAgICAgICAgICAgaWQgPSBwdHNfaWQsIAogICAgICAgICAgICAgICAgICAgYmlvbWFya2VyX3N0YXR1cyA9IAogICAgICAgICAgICAgICAgICAgICB0ZGMoYmlvbWFya2VyX3RpbWUsIGJpb21hcmtlcl9zdGF0dXMpKQoKZGF0YXRhYmxlKGR0X2ZpbmFsLCBmaWx0ZXIgPSAidG9wIikKCiMgU3ludGF4IGlmIHRoZXJlIGlzIG5vdCB0aW1lLWRlcGVuZGVudCBjb3ZhcmlhdGUKIyBmaXQgPC0gY294cGgoU3VydihkZnNfdGltZSwgZGZzX2V2ZW50KSB+IGJpb21hcmtlcl9zdGF0dXMsCiMgICAgICAgICAgICAgIGRhdGEgPSBkdF9maW5hbCkKIyBzdW1tYXJ5KGZpdCkKCmZpdCA8LSBjb3hwaChTdXJ2KHRzdGFydCwgdHN0b3AsIGRmc19ldmVudCkgfiBiaW9tYXJrZXJfc3RhdHVzLAogICAgICAgICAgICAgZGF0YSA9IGR0X2ZpbmFsKQpzdW1tYXJ5KGZpdCkKYGBgCgoKI1JGUyBieSBjdEROQSBEeW5hbWljcyBwb3N0LU5BQyB0byBXZWVrIDQgLSAzIGdyb3VwcwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKCmNpcmNfZGF0YSRjdEROQS5EeW5hbWljcyA8LSBOQSAjZmlyc3Qgd2UgY3JlYXRlIHRoZSB2YXJpYWJsZSBmb3IgdGhlIGN0RE5BICYgTkFDIGNvbWJpbmF0aW9uLCBhbmQgd2UgYXNzaWduIHZhbHVlcwpjaXJjX2RhdGEgPC0gY2lyY19kYXRhICU+JQogIG11dGF0ZShjdEROQS5EeW5hbWljcyA9IGNhc2Vfd2hlbigKICAgIGN0RE5BLnBvc3RuYWMgPT0gIk5FR0FUSVZFIiAmIGN0RE5BLjR3ID09ICJORUdBVElWRSIgfiAiMSIsCiAgICBjdEROQS5wb3N0bmFjID09ICJQT1NJVElWRSIgJiBjdEROQS40dyA9PSAiTkVHQVRJVkUiIH4gIjIiLAogICAgY3RETkEucG9zdG5hYyA9PSAiUE9TSVRJVkUiICYgY3RETkEuNHcgPT0gIlBPU0lUSVZFIiB+ICIzIgogICkpCgpjaXJjX2RhdGEgPC0gY2lyY19kYXRhWyFpcy5uYShjaXJjX2RhdGEkY3RETkEuRHluYW1pY3MpLF0Kc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUkZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50KX5jdEROQS5EeW5hbWljcywgZGF0YSA9IGNpcmNfZGF0YSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUkZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50KQpLTV9jdXJ2ZSA8LSBzdXJ2Zml0KHN1cnZfb2JqZWN0IH4gY3RETkEuRHluYW1pY3MsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJncmVlbiIsInJlZCIpLCB0aXRsZT0iUkZTIC0gY3RETkEgRHluYW1pY3MgcG9zdC1OQUMgLSBXZWVrIDQiLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gU3VyZ2VyeSIsIGxlZ2VuZC5sYWJzPWMoIlBlcnNpc3RlbnRseSBOZWdhdGl2ZSIsIkNvbnZlcnRlZCBOZWdhdGl2ZSIsICJQZXJzaXN0ZW50bHkgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAyNCkpCmNpcmNfZGF0YSRjdEROQS5EeW5hbWljcyA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLkR5bmFtaWNzLCBsZXZlbHM9YygiMSIsIjIiLCIzIiksIGxhYmVscz1jKCJQZXJzaXN0ZW50bHkgTmVnYXRpdmUiLCJDb252ZXJ0ZWQgTmVnYXRpdmUiLCAiUGVyc2lzdGVudGx5IFBvc2l0aXZlIikpCmNveF9maXQgPC0gY294cGhmKHN1cnZfb2JqZWN0IH4gY3RETkEuRHluYW1pY3MsIGRhdGE9Y2lyY19kYXRhKQpzdW1tYXJ5KGNveF9maXQpCmBgYAoKCiNSRlMgYnkgY3RETkEgRHluYW1pY3MgcG9zdC1OQUMgdG8gV2VlayA0IC0gNCBncm91cHMKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCgpjaXJjX2RhdGEkY3RETkEuRHluYW1pY3MgPC0gTkEgI2ZpcnN0IHdlIGNyZWF0ZSB0aGUgdmFyaWFibGUgZm9yIHRoZSBjdEROQSAmIE5BQyBjb21iaW5hdGlvbiwgYW5kIHdlIGFzc2lnbiB2YWx1ZXMKY2lyY19kYXRhIDwtIGNpcmNfZGF0YSAlPiUKICBtdXRhdGUoY3RETkEuRHluYW1pY3MgPSBjYXNlX3doZW4oCiAgICBjdEROQS5wb3N0bmFjID09ICJORUdBVElWRSIgJiBjdEROQS40dyA9PSAiTkVHQVRJVkUiIH4gIjEiLAogICAgY3RETkEucG9zdG5hYyA9PSAiUE9TSVRJVkUiICYgY3RETkEuNHcgPT0gIk5FR0FUSVZFIiB+ICIyIiwKICAgIGN0RE5BLnBvc3RuYWMgPT0gIk5FR0FUSVZFIiAmIGN0RE5BLjR3ID09ICJQT1NJVElWRSIgfiAiMyIsCiAgICBjdEROQS5wb3N0bmFjID09ICJQT1NJVElWRSIgJiBjdEROQS40dyA9PSAiUE9TSVRJVkUiIH4gIjQiCiAgKSkKCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbIWlzLm5hKGNpcmNfZGF0YSRjdEROQS5EeW5hbWljcyksXQpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLkR5bmFtaWNzLCBkYXRhID0gY2lyY19kYXRhKQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5EeW5hbWljcywgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsImdyZWVuIiwicHVycGxlIiwicmVkIiksIHRpdGxlPSJSRlMgLSBjdEROQSBEeW5hbWljcyBwb3N0LU5BQyAtIFdlZWsgNCIsIHlsYWI9ICJSZWN1cnJlbmNlLUZyZWUgU3Vydml2YWwiLCB4bGFiPSJNb250aHMgZnJvbSBTdXJnZXJ5IiwgbGVnZW5kLmxhYnM9YygiUGVyc2lzdGVudGx5IE5lZ2F0aXZlIiwiQ29udmVydGVkIE5lZ2F0aXZlIiwgIkNvbnZlcnRlZCBQb3NpdGl2ZSIsIlBlcnNpc3RlbnRseSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDI0KSkKY2lyY19kYXRhJGN0RE5BLkR5bmFtaWNzIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuRHluYW1pY3MsIGxldmVscz1jKCIxIiwiMiIsIjMiLCI0IiksIGxhYmVscz1jKCJQZXJzaXN0ZW50bHkgTmVnYXRpdmUiLCJDb252ZXJ0ZWQgTmVnYXRpdmUiLCJDb252ZXJ0ZWQgUG9zaXRpdmUiLCJQZXJzaXN0ZW50bHkgUG9zaXRpdmUiKSkKY294X2ZpdCA8LSBjb3hwaGYoc3Vydl9vYmplY3QgfiBjdEROQS5EeW5hbWljcywgZGF0YT1jaXJjX2RhdGEpCnN1bW1hcnkoY294X2ZpdCkKYGBgCgoKI1JGUyBieSBjdEROQSBEeW5hbWljcyBwb3N0LU5BQyB0byBNUkQgLSA0IGdyb3VwcwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKCmNpcmNfZGF0YSRjdEROQS5EeW5hbWljcyA8LSBOQSAjZmlyc3Qgd2UgY3JlYXRlIHRoZSB2YXJpYWJsZSBmb3IgdGhlIGN0RE5BICYgTkFDIGNvbWJpbmF0aW9uLCBhbmQgd2UgYXNzaWduIHZhbHVlcwpjaXJjX2RhdGEgPC0gY2lyY19kYXRhICU+JQogIG11dGF0ZShjdEROQS5EeW5hbWljcyA9IGNhc2Vfd2hlbigKICAgIGN0RE5BLnBvc3RuYWMgPT0gIk5FR0FUSVZFIiAmIGN0RE5BLk1SRCA9PSAiTkVHQVRJVkUiIH4gIjEiLAogICAgY3RETkEucG9zdG5hYyA9PSAiUE9TSVRJVkUiICYgY3RETkEuTVJEID09ICJORUdBVElWRSIgfiAiMiIsCiAgICBjdEROQS5wb3N0bmFjID09ICJORUdBVElWRSIgJiBjdEROQS5NUkQgPT0gIlBPU0lUSVZFIiB+ICIzIiwKICAgIGN0RE5BLnBvc3RuYWMgPT0gIlBPU0lUSVZFIiAmIGN0RE5BLk1SRCA9PSAiUE9TSVRJVkUiIH4gIjQiCiAgKSkKCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbIWlzLm5hKGNpcmNfZGF0YSRjdEROQS5EeW5hbWljcyksXQpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLkR5bmFtaWNzLCBkYXRhID0gY2lyY19kYXRhKQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5EeW5hbWljcywgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsImdyZWVuIiwicHVycGxlIiwicmVkIiksIHRpdGxlPSJSRlMgLSBjdEROQSBEeW5hbWljcyBwb3N0LU5BQyAtIE1SRCBXaW5kb3ciLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gU3VyZ2VyeSIsIGxlZ2VuZC5sYWJzPWMoIlBlcnNpc3RlbnRseSBOZWdhdGl2ZSIsIkNvbnZlcnRlZCBOZWdhdGl2ZSIsICJDb252ZXJ0ZWQgUG9zaXRpdmUiLCJQZXJzaXN0ZW50bHkgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAyNCkpCmNpcmNfZGF0YSRjdEROQS5EeW5hbWljcyA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLkR5bmFtaWNzLCBsZXZlbHM9YygiMSIsIjIiLCIzIiwiNCIpLCBsYWJlbHM9YygiUGVyc2lzdGVudGx5IE5lZ2F0aXZlIiwiQ29udmVydGVkIE5lZ2F0aXZlIiwiQ29udmVydGVkIFBvc2l0aXZlIiwiUGVyc2lzdGVudGx5IFBvc2l0aXZlIikpCmNveF9maXQgPC0gY294cGhmKHN1cnZfb2JqZWN0IH4gY3RETkEuRHluYW1pY3MsIGRhdGE9Y2lyY19kYXRhKQpzdW1tYXJ5KGNveF9maXQpCmBgYAoKCiNBc3NvY2lhdGlvbiBvZiBjdEROQSBwb3N0LU5BQyBhbmQgVFJHIHN0YXR1cwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKCiNWZXJ0aWNhbCBGaXNoZXIgcGxvdCBmb3IgY3RETkEgY2xlYXJhbmNlIHBvc3QtTkFUIGFuZCBSZWMgU3RhdHVzCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbIWlzLm5hKGNpcmNfZGF0YSRjdEROQS5wb3N0bmFjKSxdCmNpcmNfZGF0YSRjdEROQS5wb3N0bmFjIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEucG9zdG5hYywgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhJFRSRyA8LSBmYWN0b3IoY2lyY19kYXRhJFRSRywgbGV2ZWxzID0gYygiMSIsICIyIiwgIjMiKSwgbGFiZWxzID0gYygiVFJHMSIsICJUUkcyIiwgIlRSRzMiKSkKY29udGluZ2VuY3lfdGFibGUgPC0gdGFibGUoY2lyY19kYXRhJFRSRywgY2lyY19kYXRhJGN0RE5BLnBvc3RuYWMpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpjaGlfc3F1YXJlX3Rlc3QgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV90ZXN0KQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKCiMgU3dhcHBpbmcgeCBhbmQgeSBpbiBnZ3Bsb3QgZnVuY3Rpb24gdG8gbWFrZSBiYXIgcGxvdCB2ZXJ0aWNhbApnZ3Bsb3QodGFibGVfZGYsIGFlcyh5ID0gVmFyMSwgeCA9IFBlcmNlbnRhZ2UsIGZpbGwgPSBWYXIyKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyh4ID0gTWlkZGxlUGVyY2VudGFnZSwgbGFiZWwgPSBGcmVxKSwgcG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJibGFjayIsIHZqdXN0ID0gMS41LCBzaXplID0gNykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJjdEROQSBwb3N0LU5BVCAmIFRSRyIsIHkgPSAiVFJHIFN0YXR1cyIsIHggPSAiUGF0aWVudHMgKCUpIiwgZmlsbCA9ICJjdEROQSBwb3N0LU5BQyIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTkVHQVRJVkUiID0gImxpZ2h0Ymx1ZTMiLCAiUE9TSVRJVkUiID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBSZWN1cnJlbmNlIGxhYmVsIHNpemUKYGBgCgoKI1JGUyBieSBjdEROQSBwb3N0LU5BQyBhbmQgVFJHIC0gMyBncm91cHMKYGBge3J9CnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiTkNDIEVTQ0MgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5wb3N0bmFjIT0iIixdCgpjaXJjX2RhdGEkY3RETkEucENSIDwtIE5BCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGEgJT4lCiAgbXV0YXRlKGN0RE5BLnBDUiA9IGNhc2Vfd2hlbigKICAgIGN0RE5BLnBvc3RuYWMgPT0gIk5FR0FUSVZFIiAmIFRSRyAlaW4lIGMoMSwgMikgfiAiMSIsCiAgICBjdEROQS5wb3N0bmFjID09ICJORUdBVElWRSIgJiBUUkcgPT0gMyB+ICIyIiwKICAgIGN0RE5BLnBvc3RuYWMgPT0gIlBPU0lUSVZFIiAmIFRSRyAlaW4lIGMoMSwgMikgfiAiMyIKICApKQoKc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUkZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50KX5jdEROQS5wQ1IsIGRhdGEgPSBjaXJjX2RhdGEpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbIWlzLm5hKGNpcmNfZGF0YSRjdEROQS5wQ1IpLF0KY2lyY19kYXRhJGN0RE5BLnBDUiA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLnBDUiwgbGV2ZWxzPWMoIjEiLCIyIiwgIjMiKSwgbGFiZWxzPWMoIlRSRzEvMiBjdEROQSAoLSkiLCJUUkczIGN0RE5BICgtKSIsICJUUkcxLzIgY3RETkEgKCspIikpCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCl+Y3RETkEucENSLCBkYXRhID0gY2lyY19kYXRhKQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5wQ1IsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAyNCkpCmdnc3VydnBsb3QoS01fY3VydmUsIGRhdGEgPSBjaXJjX2RhdGEsIHB2YWwgPSBGQUxTRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIAogICAgICAgICAgIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJibHVlIiwgImdyZWVuIiwgInJlZCIpLCB0aXRsZT0iREZTIC0gY3RETkEgcG9zdC1OQUMgJiBUUkciLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gU3VyZ2VyeSIsIAogICAgICAgICAgIGxlZ2VuZC5sYWJzPWMoIlRSRzEvMiBjdEROQSAoLSkiLCJUUkczIGN0RE5BICgtKSIsICJUUkcxLzIgY3RETkEgKCspIiksIGxlZ2VuZC50aXRsZT0iIikKCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLnBvc3RuYWMhPSIiLF0KCmNpcmNfZGF0YSRjdEROQS5wQ1IgPC0gTkEKY2lyY19kYXRhIDwtIGNpcmNfZGF0YSAlPiUKICBtdXRhdGUoY3RETkEucENSID0gY2FzZV93aGVuKAogICAgY3RETkEucG9zdG5hYyA9PSAiTkVHQVRJVkUiICYgVFJHICVpbiUgYygxLCAyKSB+ICIxIiwKICAgIGN0RE5BLnBvc3RuYWMgPT0gIk5FR0FUSVZFIiAmIFRSRyA9PSAzIH4gIjIiLAogICAgY3RETkEucG9zdG5hYyA9PSAiUE9TSVRJVkUiICYgVFJHICVpbiUgYygxLCAyKSB+ICIzIgogICkpCmNpcmNfZGF0YSRjdEROQS5wQ1IgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5wQ1IsIGxldmVscz1jKCIxIiwiMiIsIjMiKSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUkZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50KQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEucENSLCBkYXRhPWNpcmNfZGF0YSkKc3VtbWFyeShjb3hfZml0KQpgYGAKCgojUkZTIGJ5IGN0RE5BIHBvc3QtTkFDIGFuZCBwQ1IgLSAzIGdyb3VwcwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuNHchPSIiLF0KY2lyY19kYXRhJFJGUy5tb250aHM9Y2lyY19kYXRhJFJGUy5tb250aHMtMQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRSRlMubW9udGhzPj0wLF0KCmNpcmNfZGF0YSRjdEROQS5wQ1IgPC0gTkEKY2lyY19kYXRhIDwtIGNpcmNfZGF0YSAlPiUKICBtdXRhdGUoY3RETkEucENSID0gY2FzZV93aGVuKAogICAgY3RETkEucG9zdG5hYyA9PSAiTkVHQVRJVkUiICYgcENSID09ICJUUlVFIiB+ICIxIiwKICAgIGN0RE5BLnBvc3RuYWMgPT0gIk5FR0FUSVZFIiAmIHBDUiA9PSAiRkFMU0UiIH4gIjIiLAogICAgY3RETkEucG9zdG5hYyA9PSAiUE9TSVRJVkUiICYgcENSID09ICJGQUxTRSIgfiAiMyIsCiAgKSkKCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCl+Y3RETkEucENSLCBkYXRhID0gY2lyY19kYXRhKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhWyFpcy5uYShjaXJjX2RhdGEkY3RETkEucENSKSxdCmNpcmNfZGF0YSRjdEROQS5wQ1IgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5wQ1IsIGxldmVscz1jKCIxIiwiMiIsICIzIiksIGxhYmVscz1jKCJwQ1IgY3RETkEgKC0pIiwiTm8gcENSIGN0RE5BICgtKSIsICJObyBwQ1IgY3RETkEgKCspIikpCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCl+Y3RETkEucENSLCBkYXRhID0gY2lyY19kYXRhKQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5wQ1IsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAyNCkpCmdnc3VydnBsb3QoS01fY3VydmUsIGRhdGEgPSBjaXJjX2RhdGEsIHB2YWwgPSBGQUxTRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIAogICAgICAgICAgIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJibHVlIiwgImdyZWVuIiwgInJlZCIpLCB0aXRsZT0iREZTIC0gY3RETkEgcG9zdC1OQUMgJiBwQ1IiLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iVGltZSAoTW9udGhzKSIsIAogICAgICAgICAgIGxlZ2VuZC5sYWJzPWMoInBDUiBjdEROQSAoLSkiLCJObyBwQ1IgY3RETkEgKC0pIiwgIk5vIHBDUiBjdEROQSAoKykiKSwgbGVnZW5kLnRpdGxlPSIiKQoKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuNHchPSIiLF0KY2lyY19kYXRhJFJGUy5tb250aHM9Y2lyY19kYXRhJFJGUy5tb250aHMtMQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRSRlMubW9udGhzPj0wLF0KCmNpcmNfZGF0YSRjdEROQS5wQ1IgPC0gTkEKY2lyY19kYXRhIDwtIGNpcmNfZGF0YSAlPiUKICBtdXRhdGUoY3RETkEucENSID0gY2FzZV93aGVuKAogICAgY3RETkEucG9zdG5hYyA9PSAiTkVHQVRJVkUiICYgcENSID09ICJUUlVFIiB+ICIxIiwKICAgIGN0RE5BLnBvc3RuYWMgPT0gIk5FR0FUSVZFIiAmIHBDUiA9PSAiRkFMU0UiIH4gIjIiLAogICAgY3RETkEucG9zdG5hYyA9PSAiUE9TSVRJVkUiICYgcENSID09ICJGQUxTRSIgfiAiMyIsCiAgKSkKY2lyY19kYXRhJGN0RE5BLnBDUiA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLnBDUiwgbGV2ZWxzPWMoIjEiLCIyIiwiMyIpKQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5wQ1IsIGRhdGE9Y2lyY19kYXRhKQpzdW1tYXJ5KGNveF9maXQpCmBgYAoKCiNNdWx0aXZhcmlhdGUgY294IHJlZ3Jlc3Npb24gZm9yIFJGUyAtIGN0RE5BIHBvc3QtTkFDCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEucG9zdG5hYyE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCmNpcmNfZGF0YSRjdEROQS5wb3N0bmFjIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEucG9zdG5hYywgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSwgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkKY2lyY19kYXRhJEFnZS5Hcm91cCA8LSBmYWN0b3IoY2lyY19kYXRhJEFnZS5Hcm91cCwgbGV2ZWxzPWMoIjEiLCIyIiksIGxhYmVscyA9IGMoIuKJpDcwIiwgIj43MCIpKQpjaXJjX2RhdGEkU3RhZ2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRTdGFnZSwgbGV2ZWxzID0gYygiSS1JSSIsICJJSUktSVYiKSkKc3Vydl9vYmplY3QgPC0gU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCkgCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5wb3N0bmFjICsgQWdlLkdyb3VwICsgU3RhZ2UsIGRhdGE9Y2lyY19kYXRhKSAKZ2dmb3Jlc3QoY294X2ZpdCwgZGF0YSA9IGNpcmNfZGF0YSwgbWFpbiA9ICJNdWx0aXZhcmlhdGUgUmVncmVzc2lvbiBNb2RlbCBmb3IgUkZTIiwgcmVmTGFiZWwgPSAiUmVmZXJlbmNlIEdyb3VwIikKdGVzdC5waCA8LSBjb3guenBoKGNveF9maXQpCmBgYAoKCiNNdWx0aXZhcmlhdGUgY294IHJlZ3Jlc3Npb24gZm9yIFJGUyAtIGN0RE5BIHdlZWsgNApgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJOQ0MgRVNDQyBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLjR3IT0iIixdCgpjaXJjX2RhdGEkY3RETkEuNHcgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS40dywgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSwgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkKY2lyY19kYXRhJEFnZS5Hcm91cCA8LSBmYWN0b3IoY2lyY19kYXRhJEFnZS5Hcm91cCwgbGV2ZWxzPWMoIjEiLCIyIiksIGxhYmVscyA9IGMoIuKJpDcwIiwgIj43MCIpKQpjaXJjX2RhdGEkU3RhZ2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRTdGFnZSwgbGV2ZWxzID0gYygiSS1JSSIsICJJSUktSVYiKSkKc3Vydl9vYmplY3QgPC0gU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCkgCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS40dyArIEFnZS5Hcm91cCArIFN0YWdlLCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsIGRhdGEgPSBjaXJjX2RhdGEsIG1haW4gPSAiTXVsdGl2YXJpYXRlIFJlZ3Jlc3Npb24gTW9kZWwgZm9yIFJGUyIsIHJlZkxhYmVsID0gIlJlZmVyZW5jZSBHcm91cCIpCnRlc3QucGggPC0gY294LnpwaChjb3hfZml0KQpgYGAKCgojTXVsdGl2YXJpYXRlIGNveCByZWdyZXNzaW9uIGZvciBSRlMgLSBjdEROQSBNUkQgV2luZG93CmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIk5DQyBFU0NDIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEIT0iIixdCgpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkQWdlLkdyb3VwIDwtIGZhY3RvcihjaXJjX2RhdGEkQWdlLkdyb3VwLCBsZXZlbHM9YygiMSIsIjIiKSwgbGFiZWxzID0gYygi4omkNzAiLCAiPjcwIikpCmNpcmNfZGF0YSRTdGFnZSA8LSBmYWN0b3IoY2lyY19kYXRhJFN0YWdlLCBsZXZlbHMgPSBjKCJJLUlJIiwgIklJSS1JViIpKQpzdXJ2X29iamVjdCA8LSBTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUkZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUkZTLkV2ZW50KSAKY294X2ZpdCA8LSBjb3hwaChzdXJ2X29iamVjdCB+IGN0RE5BLk1SRCArIEFnZS5Hcm91cCArIFN0YWdlLCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsIGRhdGEgPSBjaXJjX2RhdGEsIG1haW4gPSAiTXVsdGl2YXJpYXRlIFJlZ3Jlc3Npb24gTW9kZWwgZm9yIFJGUyIsIHJlZkxhYmVsID0gIlJlZmVyZW5jZSBHcm91cCIpCnRlc3QucGggPC0gY294LnpwaChjb3hfZml0KQpgYGAKCg==